JVM垃圾收集(一)—分代收集理论

分代收集理论

一、概述

垃圾收集算法可以划分为“引用计数式垃圾收集”(Reference Counting GC)和“追踪式垃圾收集”(Tracing GC)两大类,这两类也常被称作“直接垃圾收集”和“间接 垃圾收集”。由于引用计数式垃圾收集算法在接下来讨论到的主流Java虚拟机中均未涉及,所以接下来介绍的所有算法均属于追踪式垃圾收集的范畴。

二、名词

    部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。

部分收集又分为:

    新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单 独收集老年代的行为。另外请注意“Major GC”这个说法现在有点混淆,在不同资料上常有不同所指, 读者需按上下文区分到底是指老年代的收集还是整堆收集。 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收 集器会有这种行为。

三、分代收集理论

当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”(Generational Collection)的理论进行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,它建立在两个分 代假说之上: 1)弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的。 2)强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消 亡。 这两个分代假说共同奠定了多款常用的垃圾收集器的一致的设计原则:收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区 域之中存储。

设计者一般至少会把Java堆划分为新生代 (Young Generation)和老年代(Old Generation)两个区域,在新生代中,每次垃圾收集 时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。

但是,在垃圾回收时仍存在跨代引用的问题,因为对象不是孤立的,对象之间会存在跨代引用。有可能老年代的对象引用了新生代的对象,此时我们在回收年轻代的时候也要遍历老年代,以保证可达性分析结果的正确性。由于老年代对象很多,空间也要比年轻代大,因此也会花费一定的时间。

3)跨代引用假说(Intergenerational Reference Hypothesis):跨代引用相对于同代引用来说仅占极 少数。 可根据前两条假说逻辑推理得出的隐含推论:存在互相引用关系的两个对象,是应该倾向于同时生存或者同时消亡的。举个例子,如果某个新生代对象存在跨代引用,由于老年代对象难以消亡,该引用会使得新生代对象在收集时同样得以存活,进而在年龄增长之后晋升到老年代中,这时跨代引用也随即被消除了。

解决跨代引用的办法 只需在新生代上建立一个全局的数据结构(该结构被称 为“记忆集”,Remembered Set),这个结构把老年代划分成若干小块,标识出老年代的哪一块内存会 存在跨代引用。此后当发生Minor GC时,只有包含了跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描。

注意:值得注意的是,分代收集理论也有其缺陷,最新出现(或在实验中)的几款垃圾收集器都展现出了 面向全区域收集设计的思想,或者可以支持全区域不分代的收集的工作模式。

经验分享 程序员 微信小程序 职场和发展