跳至正文
来两杯美式
返回

堆内存区域全景:Eden/Survivor/Old + PermGen/Metaspace 演进

By 来两杯美式
发布于更新于

这是「一文拿下 GC」系列第五篇(终章)。前面讲了「判断该死 + 怎么回收 + 哪个收集器」——这一篇回到最底层:内存区域本身长什么样、Eden/Survivor/Old 怎么协作、为什么 Java 8 要废弃 PermGen 改用 Metaspace。

一、JVM 内存结构总览

JVM 内存结构:堆(Young + Old)与非堆(Metaspace + CCS + CodeCache)

两大阵营

2017 年冷知识:当年 Java 8(2014 年发布)已经把 PermGen 换成 Metaspace——这是 HotSpot 内存模型最大的一次改动。Metaspace 直接使用本地内存,不再受限于 JVM 堆。

二、堆区(Heap)详解

2.1 Eden 区

Eden 用来分配新创建的对象

TLAB(Thread Local Allocation Buffer)

2.2 Survivor 区

2.3 Tenuring Threshold(晋升阈值)

为了确定一个对象是否「足够老」,可以被提升(Promotion)到老年代

分代 GC:Eden→Survivor→Tenured,15 次 GC 周期后晋升

晋升阈值的确定

2.4 Old Generation(老年代)

经过多次 GC 仍然存活的对象晋升到这里

三、非堆区(Non-Heap)详解

3.1 PermGen(永久代)—— Java 8 之前

PermGen 是什么

PermGen 的痛点

实际上这给 Java 开发者造成了很多麻烦——因为很难去计算这块区域到底需要占用多少内存空间。预测失败导致的结果就是产生 java.lang.OutOfMemoryError: PermGen space 这种形式的错误。

除非 OutOfMemoryError 确实是内存泄漏导致的,否则就只能增加 permgen 的大小

-XX:MaxPermSize=256

3.2 Metaspace(方法区的实现)—— Java 8 之后

为什么废弃永久代(PermGen)迎来元空间(Metaspace)

《JVM 虚拟机规范》只规定了方法区的这么个概念和作用,并没有规定怎么去实现它。在其他的 JVM 中不存在永久代,而 HotSpot 中存在永久代(jdk1.7 及以前)。

废弃 PermGen 的原因

  1. 移除永久代是为了融合 HotSpot 和 JRockit 而做努力——JRockit 早就没有永久代
  2. 不再需要配置永久代——配置永久代,经常会在使用时不够用或者发生内存泄露
  3. 在使用 PermGen 时,通过 -XX:PermSize-XX:MaxPermSize 控制大小,JVM 在启动时会根据 PermSize 初始化一块连续的内存
  4. 如果设置的过大非常浪费,而设置的 MaxPermSize 也是限制好的
  5. 实际项目中,很难确定到底这个值设置多少合适而导致内存溢出

Metaspace 的特点

3.3 其他变化

字符串常量池

字符串常量由方法区中移动到堆中——这是 Java 7 开始的改动。String.intern() 的行为也变了:在 Java 7+ 真正复用堆中已存在的字符串引用,而不是在 PermGen 中存一个 copy。

CCS(Compressed Class Space,压缩类空间)

内存中指向对象实例的指针,在 LP64 系统中,需要用 64 位来表示,ILP32 的系统中则只需要 32 位——这就是在 LP64 系统中所有引用程序运行时都会比 ILP32 大 1.5 倍左右,因为指针占用空间增加了。

CompressedOops(压缩普通对象指针)

关闭参数

-XX:-UseCompressedOops          # 关闭指针压缩
-XX:-UseCompressedClassPointers  # 关闭压缩类空间段指针

CodeCache

JIT 编译的本地代码存放空间——JIT 编译器把热点字节码编译成本地机器码后存在这里。

四、4 种引用类型(JDK 1.2 引入)

引用类型描述回收时机
强引用(Strong Reference)代码中普通的引用,类似于 Object obj = new Object();引用还存在,垃圾回收器就不会回收
软引用(Soft Reference)描述非必须的对象系统要发出内存溢出异常前会将这些对象列进回收范围之中进行第二次回收——如果这次回收后还没有足够的内存将会抛出 OOM
弱引用(Weak Reference)描述非必须的对象,强度比软引用更弱被弱引用关联的对象只会生存到下一次 GC 之前——当 GC 工作,无论当前内存是否足够都会回收被弱引用关联的对象
虚引用(Phantom Reference)也被称为幽灵引用或幻影引用一个对象是否有虚引用,完全不对其生存时间造成影响也无法通过虚引用来取得对象实例。为一个对象设置虚引用关联的唯一目的是能在这个对象被收集器回收时收到一个系统通知

2017 年冷知识:当年软引用和弱引用的典型应用是本地缓存(如 Guava Cache 早期版本、WeakHashMap)和内存敏感的对象池。今天(2026 年)Ehcache 3.x、Caffeine 等本地缓存库已经很少直接用 SoftReference/WeakReference——而是自己管理淘汰策略,JVM GC 配合做兜底回收。

五、GC 系列小结

到这里「一文拿下 GC」系列 5 篇全部完成:

  1. GC 调优入门(gc-tuning-and-tools)—— 参数 + 工具 + 参考值
  2. 怎么判断对象该死(gc-object-liveness)—— 引用计数 vs 可达性
  3. 垃圾收集算法三板斧(gc-algorithms)—— 标记-清除 / 复制 / 标记-整理
  4. 垃圾收集器大阅兵(gc-collectors)—— Serial/ParNew/Parallel/CMS/G1
  5. 堆内存区域全景(gc-memory-regions)—— Eden/Survivor/Old + PermGen/Metaspace(本文)

加上之前的「一文拿下 JVM」系列 5 篇(内存区域 + 参数命令 + 逃逸分析 + OOM 工具 + 对象分配),JVM + GC 共 10 篇完整覆盖 Java 内存管理——从「JVM 怎么跑」到「JVM 怎么回收」的全链路知识。

2017 年冷知识:当年很多人把 JVM 和 GC 当作「高级话题」敬而远之——其实真正能搞懂 GC 调优的后端工程师,在团队里是凤毛麟角。今天(2026 年)虽然有 ZGC、Shenandoah、Generational ZGC 等新一代低延迟收集器,但底层原理(分代 + 标记-整理 + 复制)没变——这一系列的内容在 2026 年依然适用,只是参考值要更新。


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
所属专题
Java GC
第 5 / 5 篇
查看系列全部文章
  1. 01.GC 调优入门:参数、工具与参考值
  2. 02.GC 怎么判断对象「该死了」:引用计数 vs 可达性分析
  3. 03.垃圾收集算法三板斧:标记-清除、复制、标记-整理
  4. 04.垃圾收集器大阅兵:Serial/ParNew/Parallel/CMS/G1
  5. 05.堆内存区域全景:Eden/Survivor/Old + PermGen/Metaspace 演进

上一篇
消息队列开篇:为什么用 MQ + 主流选型对比
下一篇
垃圾收集器大阅兵:Serial/ParNew/Parallel/CMS/G1