之前说的寻找堆中"垃圾"内存的方法是使用可达性分析算法,用一系列的GC Roots去找到引用链,进而找到已经没有用的对象,但其实现起来有两个要注意的地方
解决办法:准确式GC,也就是说,当执行系统停顿下来后,并不需要一个不漏的检查完所有执行上下文和全局的引用位置,虚拟机有办法直接知道哪些地方存放着对象引用. 在HotSpot中,使用一组称为OopMap的数据结构来达到这个目的. 在类加载完得到时候, HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中的哪些位置是引用,这样,在GC扫描的时候就可以直接得知这些信息了
在OopMap的帮助下,HotSpot可以快速且准确完成GC Roots枚举,但是如果为每一条指令都生成对应的OopMap,会消耗大量的额外空间,这样的话GC的成本就会太高
实际上,并不是每条指令都有对应的OopMap, 只是在"特定的位置"记录了这些信息,这些位置称为"安全点(SafePoint)",也就是说程序不是在任何地方都可以停下来开始GC,只有到安全点的时候才能暂停. 所以我们一般选取"能让程序长时间执行"的指令作为GC时的安全点,最明显的就是指令序列复用类的指令,比如方法调用,循环跳转,异常跳转等.
对于SafePoint,还有一个需要解决的问题: 如何在GC发生时让所有线程都跑到最近的安全点再停顿下来?解决办法有两个
SafePoint保证了程序在执行时,在不太长的时间内就会遇到可以进入GC的SafePoint,但如果程序"不执行呢"? 也就是如果没有给程序分配CPU时间,线程处于挂起或阻塞状态,这时候线程无法响应JVM的中断请求,这就需要用到安全区域(Safe Region)来解决
安全区域就是指: 在一段代码片段中, 引用关系不会发生变化,在这个区域任意地方开始GC都是安全的
当线程执行到Safe Region中的代码时,首先标识自己已经进入了Safe Region, 这样当JVM开始GC时就不用管标识自己为Safe Region状态的线程了, 当线程要离开Safe Region的时候,它要检查系统是否已经完成了根节点枚举(或是整个GC过程),如果完成了,那线程就继续执行,否则就等待直到收到可以安全离开Safe Region 的信号为止
是一个单线程的收集器,使用一条收集线程去完成垃圾收集工作,并且在进行垃圾收集时,必须暂停其他所有的工作线程,直到收集结束.因为简单而高效,目前仍然是虚拟机运行在Client模式下默认的新生代收集器(采用复制算法)
其实就是Serial收集器的多线程版本,是运行在Server模式下的首选的新生代收集器,在单CPU下不一定比Serial收集器效率高,但随着CPU数量增加,对于GC时系统资源的利用是有好处的(采用复制算法)
也是一个并行的多线程收集器,但与ParNew收集器不同的是: 关注点与其他收集器不同,比如CMS收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目的是达到一个可控制的吞吐量,其实就是运行用户代码与CPU消耗时间的比值,也就是高效的利用CPU的时间,尽快完成程序的运算任务.
可以通过设置参数来控制最大垃圾收集停顿时间和吞吐量大小
还有一个开关参数,这个参数打开后,不需要手动指定新生代的大小,Eden与Survivor区的比例,晋升老年代的对象大小等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数并提供最合适的停顿时间或者最大的吞吐量, 这种机制叫做GC的自适应调节策略,自适应调节策略也是Parallel Scavenge收集器与ParNew收集器的重要区别
是Serial收集器的老年代版本,使用"标识-整理"算法,在Client模式下的虚拟机使用; 在Server模式下,可以作为CMS收集器的后备预案,在并发收集时发生Concurrent Mode Failure时使用
是Parallel Scavenge收集器的老年代版本,使用"标记-整理算法",配合Parallel Scavenge收集器,在注重吞吐量以及CPU资源敏感的场合,可以应用
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,是基于"标记-清除"算法实现的,大致过程包括:
其中,初始标记和重新标记仍然需要中断所有程序执行线程,初始标记仅仅只是标记一下能被GC Roots直接关联到的对象,速度很快;并发标记阶段就是GC Roots Tracing的过程; 而重新标记是为了修正并发标记阶段引用用户程序继续执行而导致标志产生变化的一部分的标记记录
CMS收集器优点: 并发收集,低停顿
缺点:
G1,即"Garbage First"收集器,与其他收集器相比,有以下优点
G1将堆分为若干个大小相等的独立区域(Region),新生代和老年代不再是物理隔离的了,他们都是一部分Region的集合
G1跟踪各个Region中垃圾堆积的价值大小,在后台维护一个优先列表,优先回收价值最大的Region
但一个对象分配在某个Region当中,它还可能被其他Region中的其他对象引用,这点虚拟机使用Remembered Set来避免全堆扫描,每个Region都会有对应的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中,如果是,那就通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set中. 这样在进行内存回收时,在GC根节点的枚举范围内加上Remembered Set就可以保证不需要进行全堆扫描
G1收集器的步骤