JVM内存区域按生命周期与访问模型划分为线程私有(程序计数器、虚拟机栈、本地方法栈)和线程共享(堆、方法区/元空间、运行时常量池),直接内存属堆外但影响OOM。

按线程生命周期绑定程度划分
线程私有区域必须严格跟随单个线程的启停而创建与释放——线程一启动,它就有自己的一份;线程一结束,这份内存立刻回收,不跨线程复用。
这类区域天然隔离,无需同步机制。
典型区域:
- 程序计数器:记录当前线程执行到哪条字节码指令,切换回该线程时靠它恢复位置
- 虚拟机栈:每个方法调用生成一个栈帧,局部变量、操作数栈都在里面,随方法进出自动压栈/弹栈
- 本地方法栈:专为 JNI 调用 Native 方法服务,行为模式与虚拟机栈一致
按 JVM 进程级统一管理需求划分
线程共享区域由 JVM 在启动时一次性分配,整个进程生命周期内存在,所有线程可同时读写其中的数据。
这就带来可见性、原子性和内存一致性问题,必须配合锁、volatile、CAS 等手段保障安全。
典型区域:
- 堆(Heap):所有对象实例和数组都分配在这里,GC 主要作用域,也是 OOM 最常发生的区域
- 方法区(JDK 8+ 为元空间 Metaspace):存类结构、静态变量、常量池、JIT 编译代码等,元空间使用本地内存,不受 JVM 堆大小限制
- 运行时常量池:属于方法区的一部分,存放编译期生成的各种字面量和符号引用
额外注意:直接内存不属于规范定义,但影响真实 OOM
直接内存(Direct Memory)不是《JVM 规范》规定的运行时数据区,但它被 NIO 的 ByteBuffer.allocateDirect() 频繁使用,分配在本地内存中。
它不受堆内存参数(-Xmx)控制,但受 -XX:MaxDirectMemorySize 限制。
忽略它容易导致“堆没满却报 OOM: Direct buffer memory”。
为什么这个划分对面试关键?
理解私有/共享的本质,才能准确分析问题根源:
- 栈溢出(StackOverflowError)一定发生在虚拟机栈或本地方法栈,跟线程数和单线程递归深度有关
- 堆 OOM 多因内存泄漏或大对象频繁创建,需结合堆转储(heap dump)分析对象引用链
- 元空间 OOM 常见于动态生成类(如 CGLIB、热部署、大量反射),说明类加载器未被回收
- 程序计数器永远不会 OOM——它是唯一被规范明确豁免的区域

















