JVM内存模型
JVM(HotSpot)整体架构
JVM整体由四部分构成:
- ClassLoader
- Runtime Data Area 运行时数据区
- 执行引擎子系统
- Native interface本地库接口
JVM内存结构


- 方法区:存储类信息、常量、静态变量等信息。
- 堆:存储对象实例和数组。
- 栈:(Java方法执行的内存模型),存储局部变量表、操作数栈、动态链接、方法出口等信息。
- 本地方法栈:为本地方法执行服务。
方法区(Method Area)
1. 是什么?
线程共享的内存区域。(Non Heap)
2. 作用?
存储类信息、常量、字符串常量、类静态变量、即时编译器编译后的代码。
3. 特点?
// todo
堆(Heap)
1. 是什么?
- 是被所有线程共享的内存区域,是Java虚拟机管理的内存中最大的一块,在虚拟机启动时被创建。
- 物理上不连续,逻辑上连续。(可通过-Xmx -Xms控制堆的大小)
- 从内存分配的角度看,线程共享的堆可能划分出多个线程私有的线程本地分配缓存区(Thread Local Allocation Buffer,TLAB)。
- 是垃圾收集器管理的主要区域,分为:新生代(Young)和老年代(Old),新生代又被分为(Eden、From Survivor、To Survivor)。

2. 作用?
储存对象实例、数组、字符串常量池、类静态变量。
3. 特点?
- 可能出现OOM:如果Java堆可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,那JVM将抛出OOM。
Java虚拟栈(Java Virtual Machine Stacks)
1. 是什么?
是一个线程私有的后入先出的栈(Last In First Out)。
2. 作用?
用于存储栈帧(栈帧信息见:https://www.cnblogs.com/th2009yu/p/14561678.html)。
线程启动时会创建Java虚拟栈,每个方法在执行时会在Java虚拟栈中创建一个栈帧,用于存储局部信息表、操作数栈、动态链接、方法返回地址、附加信息等。每个方法从调用到执行完成的过程,就对应着一个栈帧在Java虚拟栈中入栈到出栈的过程。
3. 特点?
- 线程私有。
- 使用的内存不需要保证是连续的。
- 虚拟机栈的大小即可以通过-Xss参数指定栈内存初始值,也可以通过计算结果动态来扩容和收缩大小。
- 可能会出现StackOverFlowError异常:如果线程请求的栈容量超过Java虚拟机允许的最大容量时,会出现StackOverFlowError异常。(一般都是方法递归没有终止条件)
- 可能会出现OutOfMemoryError异常:如果Java虚拟机可以动态拓展,并且在尝试拓展的时候无法申请到足够的内存,或者在创建新线程时没有足够的内存去创建对应的虚拟栈,就会抛出OOM。(一般都是线程启动过多)
4. Java虚拟栈执行过程
见:https://blog.csdn.net/azhegps/article/details/54092466
本地方法栈(Native Method Stacks)
1. 是什么?
是一个线程私有的后入先出的栈(Last In First Out)。
2. 作用?
当线程调用一个Java方法时,虚拟机会创建一个栈帧,并压入Java虚拟栈。
但是如果线程调用的是native方法,虚拟机会保持Java虚拟机不变,只是简单的动态链接并直接调用指定的native方法。

3. 特点?
- 线程私有,生命周期随线程。
- 可能会产生StackOverFlowError或者OutOfMemoryError。
程序计数器
1. 是什么?
是线程私有的一小块内存空间。
2. 作用?
用来记录当前线程正在执行的字节码的地址。
3. 特点?
- 线程私有。
- JVM中唯一一个没有规定任何OutOfMemory的区域。(程序计数器保存是所属线程正在执行的字节码地址,当线程执行下一条指令时,程序计数器只修改保存的地址值,并不需要申请内存保存新地址值,因此不会产生OOM)。
- 在执行本地方法时,程序计数器的值是undefined。因为native方法是通过JNI调用本地C/C++库,该方法是通过C/C++来实现,而非Java,自然无法产生字节码,所以程序计数器的值为空。

浙公网安备 33010602011771号