JVM内存模型

JVM内存模型

JVM内存模型总体来说分为五大块。分别是:java堆,方法区(1.8后叫元空间)虚拟机栈,本地方法栈,以及程序计数器。

img

1 堆

java堆是java虚拟机所管理的内存中最大的一块。它用来存放对象实例和数组。

  • 在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old ), 1.8之前还包括方法区。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。

  • 线程共享。java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。

  • java堆是垃圾收集器管理的主要区域,因此很多时候也被称作 “GC堆”(Garbage Collection Heap)。

  • java堆可以处于物理不连续的内存空间中,只要逻辑上连续即可。

  • 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,则会抛出Out Of Memory Error 。

  • 其大小通过-Xms(最小值)和-Xmx(最大值)参数设置(最大最小值都要小于1G),前者为启动时申请的最小内存,默认为操作系统物理内存的1/64,后者为JVM可申请的最大内存,默认为物理内存的1/4,默认当空余堆内存小于40%时,JVM会增大堆内存到-Xmx指定的大小,可通过-XX:MinHeapFreeRation=来指定这个比列。

  • 在我们垃圾回收的时候,我们往往将堆内存分成新生代和老生代(大小比例1:2),新生代中由Eden和Survivor0,Survivor1组成,三者的比例是8:1:1,新生代的回收机制采用复制算法,在Minor GC的时候,我们都留一个存活区用来存放存活的对象,真正进行的区域是Eden+其中一个存活区,当我们的对象时长超过一定年龄时(默认15,可以通过参数设置),将会把对象放入老生代,当然大的对象会直接进入老生代,老生代采用的回收算法是标记整理算法。

2 方法区

  • 方法区与java堆一样,是线程共享的区域,它里面存储已被虚拟机加载的类信息、字段信息、方法信息、静态变量、常量等数据。

  • 方法区类似Java的接口,而虚拟机的实现方式,在1.8之前是永久代,1.8之后是元空间。

为什么要将永久代替换成元空间呢?

因为永久代使用的是虚拟机设置的内存,有一个 JVM 本身设置的固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小,并且可以加载更多的类。

3 栈

虚拟机栈(Java Virtual Machine Stacks)描述的是java方法(也就是字节码)执行的内存模型:每个方法在执行的时候都有一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用到执行就对应了一个栈帧在虚拟机栈中入栈到出栈的过程。

  • 线程私有。生命周期与线程相同。
  • 在java虚拟机规范中,这个区域规定了两种异常状况。一是 Stack Overflow Error 异常,表示当前线程请求的栈深度大于虚拟机所允许的深度
  • 二是Out Of Memory Error 如果所在虚拟机可以扩展,当它扩展时无法得到足够的内存,则会抛出该异常。

4 本地方法栈

本地方法栈(Native Method Stack)与虚拟机栈发挥的作用是相似的,不同点在于虚拟机栈执行的是java方法服务,而本地方法栈是为虚拟机使用到的Native方法服务。有的虚拟机直接把虚拟机栈和本地方法栈合二为一,与虚拟机栈一样,本地方法栈也会抛出Stack Overflow Error和Out Of Memory Error异常。

5 程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,可看做当前线程所执行的字节码行号指令器,字节码解释器工作时就是通过改变这个计数器的值来选择下一条需要执行的字节码指令。

  • 线程私有。每个线程都需要一个独立的程序计数器,个线程之间计数器互不影响,独立存储
  • 如果线程正在执行的是一个java方法,则计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是native方法,则这个计数器为空。
  • 此内存区域是唯一一个在java虚拟机中没有规定 Out Of Memory Error情况的区域。

JVM内存模型
http://example.com/JVM内存模型/
作者
Panyurou
发布于
2022年9月14日
许可协议