JVM进程所使用的内存区域简述
文章目录
JVM进程所能使用的内存空间包括两大部分,分别是直接内存(Directory Memory;或堆外内存off-heap)、运行时数据区(JVM规范中定义的内存区域)。
了解其所使用的内存分布,以及每个区域的作用、产生的异常等信息,在遇到错误时,可以更加深入的排除问题所在。
运行时数据区(Run-Time Data Areas)
JVM进程运行时会使用多种运行时数据区域。
-
所有数据区域的生命周期和JVM进程的生命周期一致(数据区域创建于JVM进程启动,销毁于JVM进程退出)
-
有些数据区的生命周期是和JVM进程中的线程生命周期一致(这些数据区域创建于线程创建,销毁于线程退出),因此有些数据区域是线程共享的(thread shared),有些数据区域是线程私有的(thread private)。
程序计数器(The pc Register)
程序计数器,专门用来记录当前线程正在执行的JVM指令所在位置。由于JVM进程支持多线程,所以会有线程切换的操作。为了在下次切换到当前线程时,还能从上次切换前的位置继续执行,因此需要保存正确的执行位置。而那些位置数据所存放的地方就是程序计数器中。
JVM规范中的信息:
- 在任何时候,JVM线程都在执行一个方法中的指令,即该线程的当前方法。
- 如果当前方法不是本地方法(Native Method),则程序计数器会存储正在执行的JVM指令地址。 如果是本地方法,则程序计数器保存值为undefined。
该数据区域没有被规定抛出OutOfMemoryError异常的情况(可能是这块数据区域所需容量少且固定)。
栈( Java Virtual Machine Stacks)
JVM栈,JVM线程运行非本地方法时(例如Java方法),所使用的内存空间。该栈中的元素称为帧(frame)。每个栈帧中包含局部变量表、操作数栈、动态连接、方法出口等信息。线程私有。
JVM规范中的信息:
- 由于JVM栈除了push和pop frame外,从不直接操作JVM栈,所以frame可能是由heap分配的。
- 如果一个线程计算所需的JVM Stack大小超出允许范围,则JVM进程会抛出StackOverflowError异常。
- 如果一个JVM stack可以动态扩展大小,那么当需要扩展时,内存不够扩展,则JVM进程会抛出OutOfMemoryError异常。
堆(Heap)
堆,包含所有class的实例和数组的分配。所有线程共享的区域。
JVM规范中的信息:
-
存储在堆中的对象由自动存储管理系统回收(即GC,垃圾收集器),对象从不显示的重新分配。
-
JVM假定没有特定类型的自动存储管理系统,因此存储管理技术可以根据实现者的系统需求来选择。
-
如果一个计算需要的堆内存大小超出自动管理系统所能使用的大小,则JVM进程会抛出OutOfMemoryError异常。
方法区( Method Area)
方法区,用于存放class字节码中的信息。包括class中的常量池、字段、方法和构造方法中的JVM指令等信息。
JVM规范中的信息:
-
虽然方法区在逻辑上是堆的一部分,但简单的实现可以选择不对其进行垃圾回收或压缩。本规范没有规定方法区的位置或用于管理编译后的代码的策略。 (表示JVM进程在实现该内存区域时的位置,更加自由;该区域是否回收内存也可以自由选择)
-
如果方法区内存不够,则JVM进程会抛出OutOfMemoryError异常。
运行时常量区( Run-Time Constant Pool)
运行时常量区,即在方法区中存放的class字节码的部分数据,包含各种类型的常量,存放class大量信息。
JVM规范中定义产生异常的情况如下:
- 如果当创建一个Class或Interface时,导致方法区内存不够使用,则JVM进程会抛出OutOfMemoryError异常。
本地方法栈( Native Method Stacks)
本地方法栈,也就是给本地方法运行时所用的内存空间。对于Hotspot而言,就是C或C++程序运行方法时, 所用的内存空间。该空间是内存私有的。
JVM规范中定义产生异常的情况如下:
- 如果一个线程使用本地方法时,所需内存空间超出本地方法栈所剩空间大小,则JVM进程会抛出StackOverflowError异常。
- 如果本地方法栈可以动态扩展。当其尝试扩展时,内存不足;或者在为一个新线程创建新的方法栈时,内存不足。则JVM进程抛出OutOfMemoryError异常。
直接内存(Direct Memeory)
此处的直接内存指的是,JVM进程运行时数据区之外的物理内存。也称之为堆外内存(off-heap)。该内存区域不在JVM规范中定义,但JVM进程也会经常使用。 比如可以使用UnSafe包中的freeMemory方法直接分配堆外内存,或者使用JDK1.4之后的NIO类,同样可以直接使用堆外内存。
JVM规范中定义产生异常的情况如下:
- 当JVM进程要使用直接内存时,如果此时直接内存的空间不够使用,同样会产生OutOfMemoryError异常。
总结
- 内存区域关系图
- 内存区域异常关系表
StackOverflowError | OutOfMemoryError | |
---|---|---|
The pc Register | 0 | 0 |
Java Virtual Machine Stacks | 1 | 1 |
Heap | 0 | 1 |
Method Area(Run-Time Constant Pool) | 0 | 1 |
Native Method Stacks | 1 | 1 |
Direct Memory | 0 | 1 |
参考资料
《深入理解Java虚拟机》2.2
《The Java® Virtual Machine SpecificationJava SE 8 Edition》2.5
文章作者 xiaoqi
上次更新 2020-05-17