title: “JVM 分代收集理论”
date: 2022-01-20T11:58:09+08:00
draft: false
分代收集理论
当前大多数的虚拟机都遵循了“分代收集”的理论进行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,它建立在两个分代假说之上:
1)弱分代假说:绝大多数对象都是朝生夕灭的。
2)强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡。
这两个假说共同奠定了多款常用垃圾收集器的设计原则:
收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。
弱分代假说
如果一个区域中大多数对象都是朝生夕灭,难以熬过垃圾收集过程的话,那么把它们集中放在一起,每次回收时只关注如何保留少量存活而不是去标记那些大量将要被回收的对象,就能以较低代价回收到大量的空间;
强分代假说
如果剩下的都是难以消亡的对象,那把它们集中放在一块, 虚拟机便可以使用较低的频率来回收这个区域,这就同时兼顾了垃圾收集的时间开销和内存的空间有效利用。
从这里开始就可以对Java 堆划分不同的区域了,垃圾收集器才可以每次只回收其中某一个或或者某些部分的区域——因而才会有“Minor GC”“Major GC”“Full GC”这样的回收类型的划分。
故此,垃圾收集至少有新生代和老年代。
但是,根据实际情况,对象与对象之间并不是孤立的,对象之间存在跨代引用。新生代对象完全有可能被年老代对象引用。
由此,就有了第三条经验法则:“跨代引用假说”。
跨代引用假说
跨代引用相对于同代引用来说仅占极少数。
存在互相引用关系的两个对象,是应该倾向于同时生存或者同时消亡的。
举个例子,如果某个新生代对象存在跨代引用,由于老年代对象难以消亡,该引用会使得新生代对象在收集时同样得以存活,进而在年龄增长之后晋升到老年代中,这时跨代引用也随即被消除了。
-
如何对存在跨代引用的对象进行回收?
我们就不应再为了少量的跨代引用去扫?整个老年代,也不必浪费空间专门记录每一个对象是否存在及存在哪些跨代引用,只需在新生代上建立一个全局的数据结构(该结构被称为“记忆集”,Remembered Set),这个结构把老年代划分成若干小块,标识出老年代的哪一块内存会存在跨代引用。此后当发生Minor GC时,只有包含了跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描。虽然这种方法需要在对象改变引用关系(如将自己或者某个属性赋值)时维护记录数据的正确性,会增加一些运行时的开销,但比起收集时扫描整个老年代来说仍然是划算的。
垃圾收集分类
- 部分收集:指目标不是完整收集整个Java堆的垃圾收集
- 新生代收集:指目标只是新生代的垃圾收集
- 老年代收集:指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。
- 混合收集:指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。
- 整堆收集:收集整个Java堆和方法区的垃圾收集。
参考文档
《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》 - 3.3.1