Java 堆内存是垃圾回收的核心区域,JVM 把堆划分为新生代(Young)、老年代(Old/Tenured),外加堆外的元空间(Metaspace),不同区域存放不同生命周期对象,使用不同回收算法,提升 GC 效率。
一、整体内存划分(堆)
二、新生代 Young Generation
1. 作用
存放刚创建、生命周期短的对象(局部变量、临时集合、临时字符串等)。
绝大多数对象很快就会死亡,所以新生代回收频率最高、速度最快。
2. 分区比例(默认 Eden:S0:S1 = 8:1:1)
- Eden 伊甸区所有新对象优先分配在这里。Eden 占新生代 80% 空间。Eden 满了触发 Minor GC(新生代垃圾回收)。
- 两个 Survivor 幸存区(S0 / S1)同一时间只有一块是活跃区,另一块是空的复制区。Minor GC 流程:
- 标记 Eden + 活跃 Survivor 存活对象;
- 把存活对象全部复制到空的 Survivor;
- 清空 Eden 和原来的 Survivor;
- 交换两块 Survivor 的角色。
3. 对象晋升到老年代规则
对象每熬过一次 Minor GC,年龄 age +1;
- 默认年龄达到 15,直接晋升到老年代;
- 若 Survivor 中同年龄对象总和 ≥ Survivor 一半空间,等于该年龄及以上对象全部提前晋升;
- 大对象(超长数组、大字符串)直接跳过新生代,分配到老年代,避免 Eden 复制开销。
4. Minor GC 特点
- 只回收新生代;
- 使用复制算法,无内存碎片;
- 会 Stop-The-World(STW,暂停所有用户线程),但停顿很短。
- 绝大多数Eden里的对象都是垃圾,一次Minor GC后里绝大部分直接丢弃,根本不会复制
三、老年代 Old / Tenured Generation
1. 存放对象
- 新生代熬过多次 GC、年龄达标晋升的长期存活对象;
- 程序全局长生命周期对象(静态变量、缓存、数据库连接池);
- 直接分配的超大对象。
2. 回收机制
老年代空间不足时触发 Major GC / Full GC
- Major GC:仅老年代回收;
- Full GC:整堆(新生代 + 老年代)全部回收,STW 时间极长,生产环境要尽量避免。
3. 使用算法
老年代对象存活率高,不适合复制算法,主流两种:
- 标记 - 清除:标记死亡对象,直接清空,会产生内存碎片;流程两步
- 标记阶段从 GC Roots 遍历,把所有有引用、存活的对象打上标记。
- 清除阶段遍历整个堆,把没有标记的垃圾对象直接回收,释放对应内存。示意图逻辑[存活][垃圾][存活][垃圾][垃圾][存活]
标记完成后,直接删掉垃圾:
[存活] [存活] [存活]
- 产生大量内存碎片回收后空闲内存是分散的小块,当需要分配大对象时,哪怕总空闲内存足够,但没有连续空间,会直接触发 Full GC。
- 两次遍历堆,效率一般。
- 标记 - 整理:标记存活对象,全部向一侧压缩,消除碎片(CMS、G1、ZGC 常用)。流程三步
- 标记阶段:和标记清除完全一样,标记所有存活对象。
- 整理(压缩)阶段将所有存活对象向内存一端统一移动、紧密排列。
- 清理边界外全部垃圾,只留一块连续大空闲内存。示意图逻辑[存活][垃圾][存活][垃圾][垃圾][存活]
标记后全部向左压缩:
[存活][存活][存活] 大片连续空闲
- 无内存碎片,空闲内存是一整块连续空间,分配大对象无压力;
- 内存分配速度快(直接指针碰撞)。
4. 拓展:指针碰撞与空闲列表
指针碰撞(Bump-The-Pointer):适用于无碎片的连续存储空间,如新生代的eden和标记-整理后的老年代
原理
堆里维护一个分配指针(allocation pointer),指向当前空闲区域的起始位置。
- 新建对象需要内存时,直接拿指针当前地址分配;
- 指针向后滑动对象所占内存大小;
- 下一个新对象直接分配在指针新位置。
空闲列表(Free-List):适用于内存存在大量碎片(标记 - 清除后的堆),空闲内存是零散小块
原理
1. JVM 维护一张列表,记录每一块空闲内存的地址、大小;
2. 创建对象时遍历列表,找一块能放下的空闲块(类似操作系统内 存分配),并将该内存块从free list中移除
3. 需要链表查找、拆分空闲块,性能远低于指针碰撞。
四、元空间 Metaspace(非堆,不参与堆 GC)
以前 JDK7 及以下叫永久代 PermGen,JDK8 彻底移除,换成元空间。
- 不在堆内,使用本地直接内存;
- 存放:类字节码、常量池、方法信息、注解;
- 不会像永久代一样 OOM(java.lang.OutOfMemoryError,内存溢出),默认自动扩容;
- 仅在类卸载时回收,和新生代 / 老年代无关。
五、补充关键名词
- Minor GC:新生代回收
- Major GC:老年代回收
- Full GC:新生代 + 老年代 + 元空间全部回收,代价最大
- STW(Stop The World):GC 时暂停业务线程,用户程序卡死
- 对象年龄 Age:记录对象经历过多少次 Minor GC
- 晋升 Promotion:对象从新生代移动到老年代
六、完整对象生命周期示例
- new 对象 → 分配 Eden;
- Eden 满,触发 Minor GC,存活对象复制到 S0;age=1;
- 再次 Eden 满,Minor GC,存活对象复制到 S1;age=2;
- 重复 15 次 GC,age=15 → 晋升到老年代;
- 老年代空间不足 → Major GC或Full GC,清理长期存活对象。
七、为什么要分代?
降低 STW 耗时:大部分 GC 只是 Minor GC,停顿很短,Full GC 极少触发。
分治回收:短生命周期对象集中在新生代,快速清理;长生命周期对象少,老年代低频回收;
算法适配:新生代存活率低用复制算法,效率高;老年代存活率高用标记整理,节省内存;