openjdk jvm运行时内存分为六区域:线程私有(程序计数器、虚拟机栈、本地方法栈)和线程共享(堆、元空间、运行时常量池),另加规范外的直接内存;jdk8起方法区由永久代改为本地内存中的元空间,运行时常量池属元空间,字符串常量池移至堆中。

OpenJDK 的 JVM 运行时内存按《Java 虚拟机规范》划分为六个逻辑区域,其中五个在规范中明确定义,一个(直接内存)虽不在规范内但被 HotSpot 等主流实现广泛使用。JDK 8 及以后版本的关键变化是:方法区由永久代(PermGen)彻底替换为元空间(Metaspace),且 Metaspace 分配在本地内存(Native Memory),不再受 -Xmx 控制。
线程私有区域:程序计数器、虚拟机栈、本地方法栈
这三个区域生命周期与线程绑定,每个线程一份,天然无并发竞争问题。
-
程序计数器存储当前线程执行的字节码指令地址;它是唯一不会抛出OutOfMemoryError的区域,规范明确不定义任何 OOM 场景 -
虚拟机栈每个方法调用生成一个栈帧,存放局部变量表、操作数栈等;栈深度超限会抛StackOverflowError,总内存不足则可能触发OutOfMemoryError(极少见,需配合极小-Xss和大量线程) -
本地方法栈为native方法服务,行为与虚拟机栈类似,异常类型也相同
线程共享区域:堆、元空间(JDK8+)、运行时常量池
这些区域被所有线程共用,必须考虑 GC、并发访问和类卸载等问题。
-
堆是对象实例和数组的唯一分配地,GC 主战场;分代管理(Young Generation+Old Generation),默认新生代占 1/3,老年代占 2/3 -
Metaspace替代了 JDK7 的PermGen,存储类元数据(Klass)、常量池、静态变量、JIT 编译代码;它使用本地内存,OOM 错误为OutOfMemoryError: Metaspace,可通过-XX:MetaspaceSize和-XX:MaxMetaspaceSize控制 -
运行时常量池是Metaspace的一部分(不是独立内存块),并非字符串常量池的专属容器;String.intern()在 JDK7+ 后指向堆中字符串,而非永久代/元空间内的拷贝
直接内存:不在 JVM 规范内,但必须监控
通过 java.nio.DirectByteBuffer 分配,绕过堆直接使用本地内存;不受 -Xmx 限制,但受本机物理内存和 -XX:MaxDirectMemorySize 约束。
- 常见于高性能 I/O(如 Netty)、大文件映射(
MappedByteBuffer)场景 - 泄漏时表现为进程 RSS 内存持续上涨,但 JVM 堆内存正常,
jstat看不到异常,需用pmap或Native Memory Tracking (NMT)定位 - 未显式调用
cleaner或 GC 不及时,容易导致OutOfMemoryError: Direct buffer memory
真正容易被忽略的是:运行时常量池没有独立内存边界,它依附于 Metaspace;而字符串常量池(StringTable)在 JDK7+ 已移入堆中 —— 这意味着对字符串 intern 的压力会直接影响堆 GC 行为,而不是元空间。











