JVM的内存区域划分是Java虚拟机管理内存的核心机制,主要分为以下几个部分,各区域有不同的职责和特性:
1. 程序计数器(Program Counter Register)
- 作用:记录当前线程执行字节码指令的地址(行号),用于线程切换后恢复执行位置。
- 特点:
- 线程私有,每个线程独立存储。
- 唯一不会发生内存溢出(OOM) 的区域。
- 执行Native方法时,计数器值为空(Undefined)。
2. Java虚拟机栈(Java Virtual Machine Stack)
- 作用:描述Java方法执行的内存模型,每个方法调用对应一个栈帧(存储局部变量表、操作数栈、动态链接、方法出口等)。
- 特点:
- 线程私有,生命周期与线程相同。
- 局部变量表存放基本类型和对象引用(非对象本身)。
- 异常:
StackOverflowError:栈深度超过限制(如无限递归)。OutOfMemoryError:扩展栈时无法申请足够内存(如线程过多)。
3. 本地方法栈(Native Method Stack)
- 作用:为Native方法(如C/C++实现的方法)服务。
- 特点:
- 线程私有,与虚拟机栈类似。
- 由虚拟机自行决定实现方式(如HotSpot将虚拟机栈与本地方法栈合并)。
- 同样可能抛出
StackOverflowError和OutOfMemoryError。
4. Java堆(Java Heap)
- 作用:存放对象实例和数组(“几乎”所有对象在此分配内存)。
- 特点:
- 线程共享,是垃圾回收(GC)的主要区域。
- 分代管理:
- 新生代(Young Generation):Eden区、Survivor区(From/To)。
- 老年代(Old Generation):长期存活的对象。
- 异常:
OutOfMemoryError(堆无法扩展时,如内存泄漏或堆大小不足)。 - 相关参数:
-Xmx(最大堆)、-Xms(初始堆)。
5. 方法区(Method Area)
- 作用:存储类信息(Class元数据)、常量、静态变量、即时编译器编译后的代码等。
- 特点:
- 线程共享。
- JDK版本差异:
- JDK 8之前:称为“永久代”(PermGen),受JVM内存限制。
- JDK 8及之后:改为元空间(Metaspace),使用本地内存(Native Memory),避免永久代OOM问题。
- 异常:
OutOfMemoryError(如加载过多类或元空间占满本地内存)。 - 相关参数:
-XX:MetaspaceSize(初始元空间大小)。
6. 运行时常量池(Runtime Constant Pool)
- 作用:存放编译期生成的字面量和符号引用,是方法区的一部分。
- 特点:
- 动态性:运行期间可将新常量加入池(如
String.intern())。 - 异常:
OutOfMemoryError(常量池占满方法区)。
- 动态性:运行期间可将新常量加入池(如
7. 直接内存(Direct Memory)
- 作用:NIO通过
ByteBuffer.allocateDirect()分配的堆外内存(避免Java堆与Native堆间数据复制)。 - 特点:
- 不属于JVM管理的内存区域,但可能引发JVM的OOM。
- 异常:
OutOfMemoryError(本地内存不足时)。
总结图
JVM内存区域
├── 线程私有
│ ├── 程序计数器
│ ├── 虚拟机栈(栈帧)
│ └── 本地方法栈
└── 线程共享
├── 堆(对象实例)
└── 方法区(类信息、常量、静态变量)
└── 运行时常量池
常见问题
- 堆与方法区的区别:堆存对象实例,方法区存类元数据和常量。
- JDK8方法区的实现:元空间(Metaspace)替代永久代,使用本地内存。
- 直接内存与堆内存:直接内存通过Native函数分配,读写效率更高(适合频繁IO场景)。
理解这些区域有助于排查内存溢出、性能调优(如调整堆大小、避免内存泄漏)等问题。
3613

被折叠的 条评论
为什么被折叠?



