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,自然无法产生字节码,所以程序计数器的值为空。 

 

posted @ 2021-03-20 22:36  suffocat1ng  阅读(72)  评论(0)    收藏  举报