jdk 8起字符串常量池位于堆中,jdk 7已将其从永久代迁移至此,jdk 8移除永久代后改用元空间存储类元数据,但字符串常量池仍保留在堆中以利用其高效gc机制。

JDK 8 是 JVM 内存模型演进的关键分水岭。最核心的变化是永久代(PermGen)被彻底移除,取而代之的是元空间(Metaspace),同时字符串常量池也从方法区迁移到堆中。这些调整不是简单改名,而是为了解决长期存在的内存泄漏、调优困难和跨虚拟机兼容性问题。
线程私有区域基本稳定
程序计数器、虚拟机栈、本地方法栈这三块区域在 JDK 8 中保持不变,仍是线程私有、随线程生灭。
- 程序计数器仍只记录字节码行号,执行 native 方法时值为空;
- 虚拟机栈继续以栈帧为单位管理方法调用,局部变量表、操作数栈等结构未变;
- 本地方法栈服务 JNI 调用,行为与虚拟机栈类似,但不处理 Java 字节码。
堆结构延续但细节优化
堆仍是所有线程共享、存放对象实例的主区域,逻辑上仍分为新生代(Eden + Survivor)、老年代,但新增了 TLAB(ThreadLocal Allocation Buffer)机制提升分配效率。
- 对象优先在 Eden 区分配,TLAB 为每个线程划出小块私有空间,减少并发同步开销;
- 字符串常量池已不在方法区,而是位于堆中(如 String.intern() 返回的对象在堆上);
- -Xms 和 -Xmx 仍控制堆初始与最大大小,生产环境建议设为相等,避免动态扩容触发 GC。
方法区彻底重构:永久代 → 元空间
这是 JDK 8 最显著的变更。方法区的实现从 JVM 堆内划出的永久代,改为直接使用本地内存(Native Memory)的元空间。
- 类的元数据(Klass)、运行时常量池(Runtime Constant Pool)、符号引用等全部进入元空间;
- 不再受 -XX:MaxPermSize 限制,而是由 -XX:MaxMetaspaceSize 控制上限(默认无上限,易耗尽物理内存);
- 元空间可自动垃圾回收(基于类卸载),但触发条件更严格,需配合 ClassLoader 正确释放;
- 静态变量仍保留在堆中(不是元空间),因为它们属于对象状态,而非类定义信息。
直接内存与运行时数据区的关系
直接内存不属于 JVM 运行时数据区,但它由 JVM 管理(如 DirectByteBuffer 分配),且同样可能引发 OutOfMemoryError。
- NIO 的 Buffer.allocateDirect() 在本地内存中分配,堆中仅保留一个轻量级 Java 对象作引用;
- 它不受 -Xmx 限制,但受操作系统物理内存和 -XX:MaxDirectMemorySize 参数约束(默认等于 -Xmx);
- 频繁使用堆外内存时,需关注本地内存泄漏,这类 OOM 错误堆栈通常不提示“Java heap space”。











