Java内存对象是否会从老年代降级至年轻代及相关GC疑问
我的理解
- 存活时间长的对象大概率会持续被引用,因此GC优先回收年轻代,只有当年轻代内存配额耗尽时,才会处理老年代。
- GC采用标记-清除算法:从根节点开始标记所有可达对象,后续回收未被标记的对象。
核心疑问
老年代对象引用修改后的处理逻辑
老年代中的对象A引用对象B,B原本引用C,后来改为引用新对象D,此时C变为垃圾。我曾误以为修改后的B会被降级至年轻代,但这样B引用的对象也需同步降级,这种逻辑类似引用计数,和标记-清除的设计初衷相悖。另外,如果需要通过A访问B,Java如何知晓A也需降级?
脏对象扫描的衍生问题
查阅资料后了解到,老年代对象被修改时会被标记为脏对象,Minor GC时不仅从根节点,还会从脏对象开始扫描。但由此产生新疑问:若从脏对象开始扫描,是否会标记根节点不可达的循环引用?这类对象是不是要等到Major GC时才会被清理?
问题解答
老年代对象引用修改的处理逻辑
首先明确:老年代对象不会因为引用关系变更就降级到年轻代,分代的核心依据是对象的存活时长,而非引用关系的变化。当B的引用从C改为D时,只会触发一个关键操作——将B标记为脏对象。
Minor GC的目标是回收年轻代垃圾,但老年代可能持有年轻代对象的引用(比如这里B引用了新的年轻代对象D),必须保证这些引用被正确追踪。脏对象机制就是用来解决这个问题:它记录所有修改了引用关系的老年代对象,Minor GC时,除了根节点,还会遍历这些脏对象,标记它们引用的年轻代对象,避免误回收。
至于C变成垃圾的情况:如果C没有其他可达引用,会在下次覆盖到它的GC中被回收——若C在年轻代,Minor GC就能清理;若C在老年代,则需等待Major GC或混合GC处理。
脏对象扫描与循环引用的问题
脏对象扫描的前提是:脏对象本身必须是根可达的。如果一组对象是循环引用且根不可达,哪怕其中某个对象被标记为脏对象,GC标记阶段从根节点出发根本不会遍历到这个脏对象,自然不会将它们标记为可达。这类根不可达的循环引用,不管在年轻代还是老年代,最终都会在对应的GC(Minor或Major)中被清理——因为标记阶段它们不会被标记为可达,回收阶段就会被处理。
内容的提问来源于stack exchange,提问作者itsmarziparzi

