JVM内存区域划分与可能的异常

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43213517/article/details/89787990

JVM内存区域划分

Java虚拟机内存区域可以分为两部分:线程私有区域,线程共享区域。

线程私有区域
1.程序计数器

程序计数器,记录当前正在执行的字节码的地址,通俗而言就是记录当前执行到的的字节码的行号,如果当前正在执行的是一个本地方法的话,该值为空(Undifined)

程序计数器的作用

Java虚拟机的多线程是通过轮流切换CPU时间片的方式来实现的,任意时刻,一个单核处理器只能执行一个线程中的指令。为了在发生线程切换后,CPU能从正确的位置继续执行,因此需要程序计数器。程序计数器只能记录所在线程的执行行号,每个线程都有属于自己的程序计数器,因此属于线程私有的。

可能的异常或者错误

因为程序计数器只记录字节码的行号,因此所占内存空间特别小,不会发生OOM和StackOverflow。(也是唯一一块不会抛出OOM的内存区域)。

2.Java虚拟机栈

Java虚拟机栈,描述Java方法执行的内存模型,方法调用时,都会创建一个栈帧,栈帧中存储方法的局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用过程,就对应一个栈帧在虚拟机栈中入栈和出栈的过程。

局部变量表

局部变量表存储编译器可知的数据类型(8大基本数据类型和对象引用),因为Java是一门跨平台的语言,因此这些数据类型的大小在编译器间就是已知的,因此局部变量表的内存空间在编译期间就已经分配完毕,且在运行期间不会再改变。

可能的异常或者错误

当方法调用的栈深度超过虚拟机运行的栈深度时(没有出口的方法递归调用),就会抛出StackOverflow错误。一般而言栈的深度是可以扩展的,当扩展时无法再申请到内存时就会抛出OutOfMemroy错误。那么到底抛出哪个错误呢?在单线程情况下抛出Stackoverflow,多线程下抛出OOM。

3.本地方法栈

本地方法栈的作用与Java虚拟机栈的作用基本是一致的只是,本地方法栈上执行的是Native方法,而虚拟机栈上执行的是Java方法而言。而且堆本地方法栈的使用并没有严格规定,因此有的虚拟机直接将两部分合二为一。

可能的异常或者错误

与虚拟机栈一样该区域也可能抛出StackOverflow和OOM,且原因和本地方法栈是一样的。

线程共享区域

1.Java堆

Java堆在虚拟机启动时创建,是Java虚拟机管理的内存的最大的一块。Java堆是垃圾回收器管理的主要区域,因此也成为GC堆。Java堆上存放对象实例数据,和数组,但并非所有的对象实例和数组都在堆上分配。Java堆在逻辑上是连续的,在物理地址上一般是不连续的。

可能的异常或者错误

当堆中的对象所占内存达到最大值,这时候再创建新对象就会抛出OOM。堆可以通过-Xmx和-Xms来控制相应的大小。

2.方法区

方法区也是各个线程共享的区域,该区域主要存放加载的类信息,常量,静态变量,编译后的代码等。方法区也成为永久代,在JDK8更名为元空间。

永久代并不意味着数据进入方法区就永久存在,此区域的内存回收主要是针对常量池的回收以及对类型的卸载,其中对类型的卸载条件要苛刻很多。

可能的异常或者错误

当方法区无法满足内存需求时就会抛出OOM错误。

3.运行时常量池(方法区的一部分)

运行时常量池也是线程共享的内存区域,该区域保存字面量和符号引用。

字面常量:

  • 字符串(如 “abc”)
  • final修饰的变量(final int a = 10;)
  • 基本类型的值等信息(10,true,null等)。

符号引用:

  • 类和接口的全限定名;
  • 字段即属性的名称和描述符
  • 方法的名称和描述符

可能抛出的错误

运行时常量池是是方法区的一部分,因此在无法满足内存需求时,也会抛出OOM。

图示:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43213517/article/details/89787990