JVM内存模型

210 阅读4分钟

引言

在上一篇文章JVM是如何与操作系统交互的中,我们介绍了CPU是如何与JVM去交互完成一个main方法的执行的。

那么在上篇文章中所提到的JVM虚拟机中的那一块内存区域是什么样的呢?

CPU是从JVM中的什么位置获取到了什么样的指令呢?

带着以上问题,我们来了解一下什么是JVM内存模型

JVM内存模型

什么是JVM内存模型?

JVM内存模型.png

由上图可以看到所有线程共享的区域为堆和方法区两部分,而程序计数器、栈和本地方法栈则为每个线程私有的内存区域。

对于Java应用来讲,堆是虚拟机所管理的内存中最大的一块,该区域被所有线程共享,在虚拟机启动时创建,几乎所有的Java对象实例都存放在该区域。

该区域也是垃圾收集器所管理的区域,由于G1垃圾收集器之前的大部分垃圾收集器都是基于分代理论设计的,所以从内存回收的角度来看的话,该区域有新生代、老年代、永久代。从分配内存的角度来看的话,堆中可以有多个线程私有的分配缓冲区,用于提升对象分配的效率。

无论从什么角度来看,最终的目的都是为了能更好的分配内存和回收内存。当对象在该区域无法完成内存空间的分配,并且堆空间也无法再扩展时,抛出OOM异常。

方法区

方法区也是一块被各个线程共享的内存区域,用于存储已经被虚拟机加载的类型信息、常量、静态变量、即时编译后的代码缓存数据等,该区域基本不发生垃圾收集的行为,即使发生垃圾收集,收益也很小。

当该区域无法满足新的内存分配需求时,抛出OOM异常。

程序计数器

程序计数器是一块线程私有的内存空间,因为多线程是通过线程轮流切换、分配处理器的执行时间来实现的,所以在任何一个具体的时刻,处理器的一个内核都只会执行一条线程中的指令,为了让线程切换后能恢复到正确的执行位置,所以每条线程都会由一个独立的程序计数器,各个线程之间的计数器独立存储,互不影响。当线程在执行Java方法时,记录着正在执行的虚拟机字节码的指令地址;执行Native方法时,该计数器为Undefined。该区域不会抛出OOM异常。

虚拟机栈

JVM栈空间.png

和程序计数器一样,栈也是线程私有的一块内存空间,也可以叫做Java方法执行的线程内存模型,每个方法被执行时,都会同步创建一个栈帧,用于存储局部变量表、操作数栈、方法出口等信息。每个方法从被调用到执行完毕就对应着一个栈帧在虚拟机栈中的入栈和出栈的过程。

一个线程中的方法调用链可能会很长,以Java程序的角度来看,同一时刻、同一条线程里面,在 调用堆栈的所有方法都同时处于执行状态。而对于执行引擎来讲,在活动线程中,只有位于栈顶的方法才是在运行的,只有位于栈顶的栈帧才是生效的,其被称为“当前栈帧”(Current Stack Frame),与 这个栈帧所关联的方法被称为“当前方法”(Current Method)。执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作。

该内存区域异常情况:当线程请求的栈深度大于虚拟机所允许的栈深度时,抛出StackOverflowError异常;如果Java虚拟机允许动态扩展,当栈扩展时无法申请到足够的内存时,抛出OOM异常。

本地方法栈

本地方法栈与虚拟机栈作用相似,区别为本地方法栈服务于Native方法。

本文总结

class文件被加载进虚拟机后,类信息会存放在方法区,在实际运行的时候会执行方法区中的代码,在JVM中所有的线程共享堆内存和方法区,而每个线程有自己独立的Java方法栈,本地方法栈(面向native方法),PC寄存器(存放线程执行位置),当调用一个方法的时候,Java虚拟机会在当前线程对应的方法栈中压入一个栈帧,用来存放Java字节码操作数以及局部变量,这个方法执行完会弹出栈帧,一个线程会连续执行多个方法,对应不同的栈帧的压入和弹出,压入栈帧后就是JVM解释执行的过程了。

相关问题

JVM是如何完成垃圾收集的?