VM(Java虚拟机)的内存区域主要划分为以下几个部分,每个部分都有其特定的用途和生命周期:
- 程序计数器(Program Counter Register):
- 每个线程都有一个独立的程序计数器,用于记录当前线程执行到的方法内部字节码指令的位置。如果当前线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址;如果是Native方法,则计数器值为空(Undefined)。
这是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
- 每个线程都有一个独立的程序计数器,用于记录当前线程执行到的方法内部字节码指令的位置。如果当前线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址;如果是Native方法,则计数器值为空(Undefined)。
- Java虚拟机栈(Java Virtual Machine Stacks):
- 线程私有的,生命周期与线程相同。每个方法被执行时都会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每一个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。可能出现StackOverflowError
(栈深度大于JVM允许的最大深度)或OutOfMemoryError
(栈扩展失败)。
- 线程私有的,生命周期与线程相同。每个方法被执行时都会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
- 本地方法栈(Native Method Stacks):
- 类似于Java虚拟机栈,但它为执行本地方法(Native Methods)服务。不同的JVM实现可能使用不同的方式来处理本地方法栈,例如Sun HotSpot直接将本地方法栈和Java虚拟机栈合并处理。
- 堆(Heap):
- 所有线程共享的一块内存区域,是垃圾收集器管理的主要区域,几乎所有的对象实例以及数组都要在堆上分配。堆可以进一步细分为新生代(Eden空间、两个Survivor空间)和老年代。堆空间不足会导致
OutOfMemoryError: Java heap space
。
- 所有线程共享的一块内存区域,是垃圾收集器管理的主要区域,几乎所有的对象实例以及数组都要在堆上分配。堆可以进一步细分为新生代(Eden空间、两个Survivor空间)和老年代。堆空间不足会导致
- 方法区(Method Area):
- 同样是所有线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
在JDK 8之前称为“永久代”(Permanent Generation),JDK 8及之后版本则移除了永久代的概念,转而使用元空间(Metaspace),并将其放在了本地内存中。
当方法区无法满足新的内存分配需求时,也会抛出OutOfMemoryError
。
- 同样是所有线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
- 运行时常量池(Runtime Constant Pool):
- 属于方法区的一部分,用于存放编译期生成的各种字面量和符号引用,并且可以在运行期间将新的常量加入到常量池中,如String类的intern()方法。它是Class文件中常量池结构在运行时的表现形式。
- 直接内存(Direct Buffer Memory/Off-Heap Memory):
- 虽然不属于JVM规范定义的标准内存区域,但通过
java.nio.ByteBuffer.allocateDirect()
申请的直接内存也是JVM内存模型中的一个重要组成部分。
它不会占用JVM堆的空间,而是直接向操作系统申请内存,适用于需要大量I/O操作的场景。直接内存不足会引发OutOfMemoryError: Direct buffer memory
。
- 虽然不属于JVM规范定义的标准内存区域,但通过
理解这些内存区域的作用和特性对于编写高效且无误的Java程序至关重要,特别是在考虑性能优化、内存管理和异常处理等方面。
THE END