JDK6→JDK8 永久代被移除,元空间替代
·
JDK6→JDK8 的核心变化是永久代被移除,元空间替代,而常量池的归属 / 存储位置是随这个核心变化调整的,先把常量池、运行时常量池、永久代、元空间的关系和版本变迁讲清楚,再总结核心变化:
先明确 3 个核心概念(避免混淆)
- Class 文件常量池:存于字节码文件(.class) 中,是编译期生成的常量(字面量、符号引用等),属于文件级,和 JVM 内存区域无关;
- 运行时常量池:Class 文件被类加载器加载后,其常量池会被加载到 JVM 内存中,形成运行时常量池,每个类对应一个,属于内存级;
- 字符串常量池:JVM 全局唯一的常量池,存字符串字面量的引用(JDK7 后存堆对象引用),核心解决字符串重复创建问题,和运行时常量池是两个独立的内存区域(很多人会搞混)。
关键:JDK6、JDK7、JDK8 内存区域与常量池的归属变化
核心是运行时常量池 + 类元数据(字节码相关) 的存储变迁,结合字符串常量池一起讲,逻辑更完整:
1. JDK6 版本(永久代存在)
- 永久代(PermGen):属于堆的永久区,存 3 类核心数据:类元数据(字节码的结构化信息) + 运行时常量池 + 静态变量;
- 字符串常量池:单独存于永久代中,和运行时常量池同区域但独立;
- 特点:永久代有固定内存上限(默认 / 手动指定),容易 OOM,且与堆共享虚拟机内存。
2. JDK7 版本(永久代未移除,但做了核心迁移)
- 永久代仍存在,但字符串常量池被从永久代迁移到堆(Heap)(这是 JDK8 的铺垫,先释放永久代的内存压力);
- 运行时常量池、类元数据、静态变量仍留在永久代;
- 特点:解决了字符串常量池导致的永久代 OOM,但永久代本身的问题(固定上限、内存管理复杂)仍存在。
3. JDK8 版本(永久代彻底移除,元空间 Metaspace 替代)
- 永久代被完全移除,取而代之的是元空间(Metaspace);
- 元空间:属于本地内存(直接内存,非 JVM 堆内存),存类元数据(字节码相关) + 运行时常量池;
- 字符串常量池:延续 JDK7 的设计,留在堆中;
- 静态变量:从原永久代迁移到堆的方法区(非堆概念取消) 对应类的 Class 对象中;
- 特点:元空间直接使用本地内存,默认无固定上限(受物理内存限制),彻底解决了永久代的 OOM 问题,内存管理更高效。
精准修正
「常量池留下来,运行时和字节码带回元空间」→ 正确的表述是:
- Class 文件常量池:始终是字节码的一部分,一直存在,加载后才会转为运行时常量池(和 JDK 版本无关);
- 运行时常量池:JDK6 在永久代,JDK8 随类元数据(字节码)一起迁移到元空间,并非 “带回”,而是换了存储区域;
- 字符串常量池:JDK7 就从永久代迁到堆,JDK8 继续留在堆,不属于元空间(这是最容易踩的坑);
- 元空间承接的是原永久代的核心职责:存储类元数据 + 运行时常量池,而非 “带回”—— 本质是永久代的替代者,只是存储位置从 JVM 堆变为本地内存。
核心总结(JDK6→JDK8 核心变迁一句话讲清)
JDK6:类元数据 + 运行时常量池 + 字符串常量池 都在堆的永久代;JDK7:字符串常量池迁堆,永久代保留类元数据 + 运行时常量池;JDK8:永久代彻底移除,类元数据 + 运行时常量池迁本地内存的元空间,字符串常量池留在堆,静态变量迁堆的 Class 对象。
补充:元空间和永久代的核心区别(为什么要替换)
| 特性 | 永久代(JDK6-7) | 元空间(JDK8+) |
|---|---|---|
| 存储位置 | JVM 堆内存(永久区) | 本地内存(直接内存,非 JVM 堆) |
| 内存上限 | 固定上限(需手动指定 - XX:MaxPermSize) | 无默认固定上限(受物理内存限制) |
| 内存管理 | JVM 自身管理,与堆内存耦合 | 操作系统管理,与 JVM 堆解耦 |
| OOM 风险 | 高(常量池 / 类加载过多易满) | 低(仅受物理内存限制) |
简单说,JDK8 用元空间替代永久代,核心是解决永久代的内存瓶颈问题,而常量池的迁移是随这个核心变化的配套调整,其中字符串常量池的迁移提前到了 JDK7,是 JVM 团队的分步优化。
更多推荐




所有评论(0)