JVM内存分为线程私有(程序计数器、虚拟机栈、本地方法栈)和线程共享(堆、方法区/元空间、运行时常量池、直接内存)两大类;前者随线程生灭、无数据竞争,后者由JVM统一管理、需注意同步与GC。

JVM 内存区域按是否被线程共享,分为线程私有和线程共享两大类。理解这种划分,是分析内存泄漏、GC 行为和多线程问题的关键。
线程私有区域:每个线程独占一份
这些区域随线程创建而分配,随线程结束而销毁,不与其他线程共享,因此不存在数据竞争问题。
- 程序计数器(PC Register):记录当前线程执行的字节码行号。唯一不会发生 OutOfMemoryError 的区域。
- Java 虚拟机栈(Java Virtual Machine Stack):存储方法调用的栈帧,包括局部变量表、操作数栈、动态链接、方法出口等。每个方法调用对应一个栈帧入栈,返回则出栈。
- 本地方法栈(Native Method Stack):为 JVM 调用 Native 方法(如 JNI)服务,作用类似虚拟机栈,具体实现可与虚拟机栈合并(如 HotSpot 就将二者合一)。
线程共享区域:所有线程共用同一份
这些区域由 JVM 在启动时创建,生命周期与 JVM 相同,多个线程可同时访问其中的数据,需注意同步与可见性问题。
- 堆(Heap):存放对象实例和数组,是 GC 主要管理区域。几乎所有通过 new 创建的对象都在这里分配(逃逸分析优化除外)。
- 方法区(Method Area):存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。JDK 8+ 中,该区域逻辑上由 元空间(Metaspace) 实现,使用本地内存而非堆内存;运行时常量池(Runtime Constant Pool)作为其一部分,也位于此。
直接内存(Direct Memory):不受 JVM 内存模型直接管理
它不属于运行时数据区的一部分,但频繁用于 NIO 的堆外缓存(如 ByteBuffer.allocateDirect)。虽然不归 JVM 管理,但受限于本机总内存和 -XX:MaxDirectMemorySize 参数,超限会触发 OOM。
补充说明:字符串常量池与运行时常量池的位置变化
JDK 7 及以后,字符串常量池(String Table)已从方法区(永久代)移到堆中;而类的符号引用、数字字面量等仍保留在方法区(元空间)的运行时常量池内。这意味着 intern() 的字符串对象本身在堆上,其引用被记录在堆中的字符串常量池里。
搞清哪些区域线程私有、哪些共享,能帮你准确判断变量生命周期、锁的作用范围、GC 是否能回收某对象,以及为什么 static 变量会被所有线程看到——本质上,是因为它们都落在共享的堆或方法区里。









