Java堆采用分代收集策略,按对象生命周期划分为新生代(占1/3,含Eden区80%和两个Survivor区各10%)与老年代(占2/3),新生代用复制算法高效回收短命对象,老年代用标记-整理算法保障空间连续并减少碎片,辅以卡表机制优化跨代引用扫描。

Java堆采用分代收集策略,核心是按对象生命周期长短划分内存区域,并为不同区域匹配最适合的回收算法——不是为了“分类而分类”,而是让每次GC更高效、停顿更可控。
新生代:短命对象集中地,用复制算法快速清理
新生代占堆约1/3,内部细分为Eden(80%)和两个Survivor区(各10%,From与To)。绝大多数新对象在Eden分配。当Eden满时触发Minor GC:
- 存活对象被复制到一个空的Survivor区(比如To),同时年龄+1;
- Eden和原Survivor(From)被清空;
- 两个Survivor角色每次GC后交换,始终保证一块空闲用于复制;
- 对象在Survivor中每熬过一次GC,年龄加1;默认达到15岁(可通过-XX:MaxTenuringThreshold调整)或Survivor空间不足时,直接晋升至老年代;
- 大对象(如长数组)可能直接分配到老年代,避免在新生代反复复制。
老年代:长寿命对象栖息地,用标记-整理算法保障空间连续
老年代占堆约2/3,存放经过多次Minor GC仍存活的对象,以及直接分配的大对象。这里对象存活率高,GC频率低但影响大:
- 采用标记-清除-整理(Mark-Sweep-Compact)三阶段回收:先标记所有可达对象,再清除不可达对象,最后将存活对象向一端压缩,消除内存碎片;
- Major GC(或Full GC的一部分)通常比Minor GC耗时更长,因为要扫描更大区域并执行内存移动;
- 老年代空间不足时,会触发Major GC;若回收后仍无法满足分配需求,则可能抛出OutOfMemoryError。
跨代引用处理:靠卡表(Card Table)减少扫描开销
虽然新生代对象大多短命,但老年代对象可能持有对新生代对象的引用(如缓存容器引用临时数据)。若每次Minor GC都扫描整个老年代找引用,代价太高:
立即学习“Java免费学习笔记(深入)”;
- JVM用“卡表”机制把老年代划分为若干个512B大小的卡页(Card);
- 当老年代对象引用新生代对象时,对应卡页被标记为“脏”;
- Minor GC时只扫描“脏卡”对应的老年代区域,大幅缩小根节点扫描范围;
- 这是支撑分代理论高效落地的关键辅助结构,不显式暴露但不可或缺。
算法选择背后的真实约束
分代不是固定教条,而是基于两个经验假说的工程权衡:
- 弱分代假说:90%以上对象朝生夕灭 → 新生代适合复制算法(只复制存活对象,效率高);
- 强分代假说:活过几次GC的对象大概率会长期存活 → 老年代适合标记整理(减少碎片,适配长期驻留);
- 跨代引用极少 → 卡表能以极小代价维护准确性;
- 堆大小、对象分配速率、晋升阈值等参数均可调,意味着分代策略可随业务负载动态适配,而非一刀切。


















