java vm_Java VM 内存模型

本文介绍了Java VM的内存模型,包括线程私有的程序计数器、虚拟机栈、本地方法栈,以及共享数据区的GC堆、方法区。详细讨论了各区域的功能、异常情况及HotSpot虚拟机的实现。内容主要基于Java 1.8,特别是对运行时常量池和String.intern()的变化进行了说明。
摘要由CSDN通过智能技术生成

基于

java -version

java version "1.8.0_151"

Java(TM) SE Runtime Environment (build 1.8.0_151-b12)

Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

Java VM 内存模型分为线程私有和共享数据区两大类。

线程私有的包括:

程序计数器

虚拟机栈

本地方法栈

共享数据区(所有线程共享的区域)包括:

GC 堆

方法区

da33fc12c05b8a84e62e6d6c7bdcd5ac.png

程序计数器

程序计数器指当前线程所执行的字节码的行号指示器。每个线程都有自己计数器,是私有内存空间,该区域是整个内存中较小的一块。

5f0954c12722baad6d01b630a81b775d.png

当线程正在执行一个Java方法时,PC计数器记录的是正在执行的虚拟机字节码的地址;当线程正在执行的一个Native方法时,PC计数器则为空(Undefined)。

此内存区域是唯一一个在Java虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。

虚拟机栈

Java 虚拟机栈的生命周期与线程相同,是Java方法执行的内存模型。每个方法(不包含native方法)执行的同时都会创建一个栈帧结构,方法执行过程,对应着虚拟机栈帧的入栈到出栈的过程。

8efa0588f136de39a28a86f1d2ac0971.png

栈帧(Stack Frame)结构

局部变量表 (locals大小,编译期确定),一组变量存储空间, 容量以 slot 为最小单位。其中64位长度的 long 和 double 类型的数据会占用2个局部变量空间,其余的数据类型只占用一个。

操作数栈(stack大小,编译期确定),操作数栈元素的数据类型必须与字节码指令序列严格匹配

055426d151625c9c2b32f1657c610cb3.png

对于以上代码:

需要执行 x + y,所以栈深度为2。

x,y,z以及this需要4个局部变量表中的slot。

istore_0 –> this

istore_1 –> x

istore_2 –> y

istore_3 –> z

动态连接, 指向运行时常量池中该栈帧所属方法的引用,为了动态连接使用。

方法返回地址

正常退出,执行引擎遇到方法返回的字节码,将返回值传递给调用者

异常退出,遇到Exception,并且方法未捕捉异常,那么不会有任何返回值。

额外附加信息,虚拟机规范没有明确规定,由具体虚拟机实现。

Java虚拟机规范规定该区域有两种异常:

StackOverFlowError:当线程请求栈深度超出虚拟机栈所允许的深度时抛出

OutOfMemoryError:当Java虚拟机进行动态扩展无法申请足够内存时抛出

本地方法栈

本地方法栈为虚拟机使用到的 Native 方法提供服务,而前面讲的虚拟机栈则为 Java 方法提供服务。

有些虚拟机的实现直接把本地方法栈和虚拟机栈合二为一,比如非常典型的 HotSpot 虚拟机。

异常(Exception):Java虚拟机规范规定该区域可抛出StackOverFlowError 和 OutOfMemoryError。

GC 堆

4dd4cba449f4fbd238d41598ef0033cb.png

方法区

存储类的元数据的区域。

f0b3ed7d2e08d97eeead4a494ab96f51.png

栈帧中保存的对象的引用,指向堆区的对象。

对象头中保存类的元数据(方法区中)的引用

481f4cc45741c0f2553101fe6cdab385.png

此处的描述是针对 JDK7 以前,JDK7 及以后静态变量存储在 Class 对象中。

毕竟 Class 对象在JVM虚拟机中只存在一个,满足静态变量的语义。

b9053e70267e849aca33c22f13769b5b.png

cd201d414482d6a28f162d26655e93df.png

根据虚拟机的不同,方法区会有不同的实现:永久代(JDK7以前)以及MetaSpace。(不知道可不可以这么描述?)

异常(Exception):Java虚拟机规范规定该区域可抛出OutOfMemoryError。

运行时常量池

运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后进入方法区的运行时常量池。

字面量:与Java语言层面的常量概念相近,包含文本字符串、声明为final的常量值等。

符号引用:编译语言层面的概念,包括以下3类:

类和接口的全限定名

字段的名称和描述符

方法的名称和描述符

7ddc1b2db120e75c1b84ce007ce783ad.png

以上摘自周志明的深入理解Java虚拟机(第2版)

是以 JDK7 为宿主环境。

在 JDK7 以后 String.intern() 发生了些许变化。

由于字符串常量 保存在 String Literal Pool(HotSpot实现为StringTable),此处保存的也仅仅是字符串对象的引用,真正的 String 类型的对象保存在堆中。

由 String.intern() 运行时动态产生的驻留字符串对象是不是就和运行时常量池没关系了呢?

c47a44d86aaa6c01b62b483fb4f8e578.png

52ecd81de78d6f6610e76cf407257c3d.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值