标记-清除算法
算法分为:标记和清除,两个阶段。
首先标记出所有需要回收的对象(用可达性分析),在标记完成后同一回收所有被标记的对象,它的标记过程和前一节讲的对象标记判定时已经介绍过了。
有两个不足之处:第一,效率问题,标记和清除两个过程的效率都不高;第二,空间碎片问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另外一次垃圾收集动作。

这是最基础的算法,后续的所有收集算法都是基于这种思路并加以改进的。
复制算法
为了解决效率的问题。
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的那块内存清理掉。
这样,每次都说对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可。
实现简单,运行高效。
但这种算法将可用内存缩小为原来的一半。

商业虚拟机都是采用复制算法来回收新生代的,新生代中的对象有98%是“朝生夕死”的,所以并不需要安装1:1的比例来划分内存空间。
而是将内存划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性地复制到另外一块Survivor上,最后清理掉Eden和之前使用的Survivor空间。
HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是说,每次新生代可用内存空间为整个新生代容量的90%。
但我们无法保证每次要回收的对象不多于10%,当Survivor空间不够时,需要依赖其他内存(这里指老年代)进行内存担保。
即,如果另外一块Survivor空间没有足够的空间存放上一次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老年代。
标记-整理算法
复制算法在对象存活率比较高的时候就要进行较多的复制操作,效率将会降低,而且需要额外的空间进行内存担保。所以在老年代一般不能直接选用这种算法。
根据老年代的特点,提出了“标记-整理”算法。标记过程任然和“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行整理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

增量算法
对于大部分的垃圾回收算法而言,在垃圾回收过程中,其他的线程将处于"Stop the World"的状态,即被挂起,暂停一切工作,等待垃圾回收的完成。
如果垃圾回收的时间过长,线程会被挂起很久;
增量算法,让垃圾收集线程和其他线程交替执行,每次垃圾回收线程只回收一小片区域的内存空间,直至垃圾回收完成;这种方式能减少系统的停顿时间,但因为线程切换和上下文转换的消耗,会使垃圾回收的陈本上升,造成系统吞吐量的下降;
分代收集算法
商业虚拟机的垃圾收集都是采用“分代收集”算法。
根据对象存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代,根据各个年代的特点采用最适合的垃圾收集算法。
新生代就采用复制算法,因为每次垃圾收集都有大量的对象死去,只有少量存活,只要付出少量存活对象的复制成本就可以完成收集了。
而老年代中因为对象存活率高,没有额外空间对它进行分配担保,就必须使用“标记-清理”或者“标记-整理”算法来进行回收。
HotSpot中的具体实现
上面从理论上介绍了对象存活判定算法和垃圾回收算法,而HotSpot在实现这些算法的时候,必须考虑算法的执行效率,这才能保证虚拟机的高效运行。
枚举根节点
可达性分析中,从GC Roots节点找引用链时,可作为GC Roots的节点主要是在全局性的引用(例如常量或者类静态属性)与执行上下文(例如,栈帧中的本地变量表)中。
但现在很多应用仅仅方法区就有数百兆,如果要逐个检查这里面的引用,那么需要消耗很多的时间。
另外,可达性分析对执行时间的敏感还体现在GC停顿上,因为这项分析必须在一个能确保一致性的快照中进行。这里的一致性指的是整个分析期间,整个系统看起来像是被冻结在某个时间点上,不可以出现,分析过程中对象的引用还在不断地发生变化。
这点是导致GC进行时必须停顿所有的Java执行线程(Sun将这件事情成为“Stop The World”)的其中一个原因,即使在号称(几乎)不会发生停顿的CMS收集器中,枚举根节点时也必须要停顿。
由于虚拟机使用的都是准确式GC,所以当执行系统停顿下来后,并不需要一个不漏地检查完所有执行上下文和全局的引用位置,虚拟机是有办法直接得知哪些地方存放着对象引用。
在HotSpot中,是使用一组称为OopMap的数据结构来达到这个目的的。在类加载完成的时候,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。
这样,GC在扫描的时候就可以直接得知这些信息了。
安全点
在OopMap的协助下,HotSpot可以快速且准确地完成GC Roots枚举,但又出现了一个问题,可能导致引用关系的变化,或者说OopMap内容变化的指令非常多。如果为每一条指令都生成对应的OopMap,那将会需要大量的额外空间,这样GC的克空间成本将会很高。
安全区域
Mark.还需补充
本文介绍了几种常见的垃圾回收算法,包括标记-清除算法、复制算法、标记-整理算法及增量算法等,并探讨了它们的工作原理、优缺点以及应用场景。此外,还详细解释了HotSpot虚拟机如何实现这些算法。
1711

被折叠的 条评论
为什么被折叠?



