详解JVM运行时数据区
1.前言:什么是运行时数据区
官网解释 :The Java Virtual Machine defines various run-time data areas that are used during execution of a program. 大意为:Java虚拟机定义了程序执行期间使用的各种运行时数据区域。 我对运行时数据区的理解是:JVM 在运行时将该Java 进程占用的内存进行逻辑划分,划分为若干个不同的数据区域,这些数据区域就组成了JVM 的运行时数据区 。
2.运行时数据区都包含哪些内容
运行时数据区总览: 我们可以按照线程共享 和线程独享 进行分类:
- 线程共享:方法区、堆
- 线程独享:虚拟机栈、本地方法栈、程序计数器
计算机的组成按照数据流 和指令流 区分,那么运行时数据区也可以按照数据流 和指令流 来区分: - 数据流:方法区、堆
- 指令流:虚拟机栈、本地方法栈、程序计数器
下面我们就来看下各个区域都包含了什么东西。
2.1 方法区
方法区用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。方法区是一个定义,我们可以把它理解为Java 里面的接口,而怎么实现是由虚拟机厂商来决定的。在JDK7 中HotSpot 是用永久代 来实现方法区的,但是JDK7 中HotSpot 移除了永久代 ,是用MetaSpace 来实现方法区。 根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OutOfMemory Error 异常。
2.2 堆
Java 中大部分对象的实例都是放在堆中的。由于现代垃圾回收器都是基于分代收集垃圾设计的,所以我们可以按分代来分类:
- 新生代:里面又分为Eden区、S0、S1等区域
- 老年代
由于JDK7 的永久代 和JDK8 及其以后的MetaSpace 属于上面章节讲过的方法区 ,所以这里就没有再讲了。 Java堆 既可以被实现成固定大小的,也可以是可扩展的,如果在Java堆 中没有足够的内存来完成实例分配,并且也无法扩展时,JVM 将会抛出OutOfMemory Error 异常。
2.3 程序计数器
上面提过,程序计数器是线程独享的,它记录的是当前线程所执行的字节码的行号。比如当前线程的时间片到了,操作系统要进行线程切换,那么你要知道当前线程执行到哪一行了,下次操作系统切换回来的时候,才能知道从哪一行继续执行。 如果线程正在的执行的是Java 方法,这个计数器记录的是虚拟机字节码指令的地址;如果正在执行的是本地(Native )方法,这个计数器值应该为空。由于程序计数器只是记录行号,所占内存非常小,一般情况下也不会内存溢出,所以它是《Java虚拟机规范》中唯一一个没有规定任何OutOfMemory Error 异常情况的区域。
2.4 Java虚拟机栈
虚拟机栈描述的是方法执行时的内存模型:每个方法被执行的时候,JVM 都会同步创建一个栈帧 用于存储局部变量表 、操作数栈 、动态连接 、方法出口 等信息。
- 局部变量表:存放的是方法内的局部变量。如果是基本类型,直接存放在
局部变量表 中;如果是对象类型,则存放的是对象的指针(引用)。局部变量表是按槽(Slot)来表示的,比如定义一个变量a,取的是时候不用再去找a了,是从对应的某个槽中取值。 - 操作数栈:JVM 的执行引擎是基于栈的执行引擎,其中的栈指的就是操作栈。操作数栈可理解为Java虚拟机栈中的一个用于计算的临时数据存储区,比如从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者。
- 动态连接:首先要理解什么是静态连接:比如我们在调用一个方法的时候,在编译期就能确定要调用的是哪个方法,就可以将符号引用转为直接引用,在运行时根据直接引用的地址去调用方法即可。那动态连接就是在编译时是不确定的,比如Java的多态,可能有多个实现类,在编译时期可能是不知道最终要调用哪个实现类的方法的,只能在程序运行期间根据实际的类型绑定相关的方法,这个就是动态连接。
- 方法出口:可以理解为方法返回值(返回地址),根据返回类型可以分为:
- 正常返回:无异常
- 异常返回:有异常
在《Java虚拟机规范》中,对这个内存区域规定了两类异常状况:
- 线程中栈的深入大于虚拟机所允许的深度,将抛出
StockOverflow Error 异常。 - 当栈无法申请到足够的内存将会抛出
OutOfMemory Error 异常。
2.5 本地方法栈
本地方法栈与虚拟机栈所发挥的作用是非常相似的,虚拟机栈是为Java 方法服务,本地方法栈是为native 方法服务。 比如我们常用的java.lang.Object#getClass 就是native 方法:
public final native Class<?> getClass();
3 总结
运行时数据区包含了方法区、堆、虚拟机栈、本地方法栈、程序计数器等区域。知道了各个区域里面包含的内容,我们就能更清楚的了解Java 程序的执行过程,看到代码我们的大脑中就能知道这个变量存储在哪个区域,如果抛出异常我们也能知道是哪个区域抛出的异常。还能了解到虚拟机指令的执行过程,会让我们对Java 虚拟机 有更深刻的了解。
参考:《深入理解Java虚拟机》
好了,这篇文章就到这里了,感谢大家的观看!如有错误,请及时指正!欢迎大家关注我的公众号:贾哇技术指南 。
/
|