JVM | 运行时数据区的 JVM 规范

本贴最后更新于 1756 天前,其中的信息可能已经天翻地覆

本文知识点

  • JVM 虚拟机制定的规范
  • 方法区,永久代,元空间的区别

参考文档

总述

在这一块的学习时, 我们容易陷入一个误区,就是一上来就直接搜索运行时数据区, 网上有些文章对虚拟机规范和 HOTSPOT 实现没有区分开,导致有时候大家看的两篇文章解释尽不一样。 自己也容易糊涂。 所以本篇特地将两个拆开讲。 且尽量以官方文档为准

我们可以把 jvm 规范理解成接口。 就是要这些东西, 然后不同的虚拟机厂商有不同的实现方案。 如方法区,hotspot 用了 1.7 及以前用了永久代, 1.8 及以后用了元数据区。 别的虚拟机如 JRocket,J9 都没有永久代的概念。

JVM 运行时数据区制定的虚拟机规范

如上参考 pdf 中及下图所示,主要有六大数据区域:

image.png

The pc Register | program Counter Register | 程序计数器

程序计数器为线程私有的,每个线程都有自己独立的程序计数器

如果当前线程执行的是 Java 方法,则程序计数器中是当前执行虚所机字节码指令的地址,如果正在执行是 native 方法,这个计数器的值是空的

我们假设有以下场景。 此时有两个线程 A,B 正在执行。

未命名作品

CPU 执行线程 A 的 Ia1 指令时, Ta 的程序计数器存的是 Ia1 指令的地址, 执行完指令 Ia1 后,转而执行线程 B 的 Ib1 指令, 再回到线程 A 时, 从程序计数器中取出上次执行到了 Ia1, 然后继续往下执行。

Java Virtual Machine Stacks | Java 虚拟机栈

Java 虚拟机栈也是线程私有的, 该线程每调用一个方法,都用创建一个栈帧(Frame).栈帧中有局部变量表,操作数栈,动态链接,方法出口等信息。

开发中遇到和虚拟机栈相关的问题:

  1. i++ 线程不安全

    我们常说的 i++ 线程不安全问题, 其根本就在于栈帧中的局部变量表,操作数栈这两个结构。

  2. 递归太多 StackOverFlow

    递归就是自己调用自己,每调用一次,就是创建一个栈帧添加到虚拟机栈中, 添加的多了,超过了容量,就会报如上 StackOverFlow 的错误

我们在 idea 的 debug 界面也可以看到关于栈和栈帧相关界面,如下图所示:

image.png

1:栈帧列表

2:可以切换不同的线程,看对应的栈帧

3:当前栈帧中的用到的变量

Heap 堆

所以线程共享的一块区域,几乎所以有 Java 对象都在堆里面进行分配,这里要注意以下几个问题

  1. 并不是所有的对象都在堆中分配

    这是一个很容易被忽略的点,jdk1.8 之后,虚拟机默认开启子逃逸分析,如果变量 A 只在本方法中使用,则可以不在堆中分其分配,可以在栈中为其分配。 这样随着方法调用结束,栈帧销毁,对角也跟着销毁,就不用调用 GC 了

  2. 虚拟机规范并没有对堆进行分代划分

    如我们现在常说的年轻代,老年代等是 HotSpot 的实现, JVM 规范只是制定了堆,没有制定分代的标准。

Run-Time Constant Pool | 运行时常量池

运行时常量池是方法区的一部分,与之对应是。class 文件中的静态常量信息,如下图所示:

image.png

在 class 文件加载的链接步骤中的解析阶段,会把静态的常量池和运行时常量池关联起来,把符号引用变成直接引用。

Method Area | 方法区

方法区也是被线程所共享的,其实是从堆里面划出来的一片区域(这里不要钻是从哪个代里面划出来的, 如上据说,JVM 规范并没有规定分代的,由各个实际的虚机机去实现的,可自己去看怎么划分)

里面存放的有:已被虚拟机加载的类信息, 常量,静态变量,即时编译器编译后的代码缓存

Native Method Stacks | 本地方法栈

这个和上面的 Java 虚拟机栈没太大的差别, 在 jvm 规范层面,把本地方法栈描述为在 Java 调用其他语言写的方法时创建,在 HotSpot 实现层面, 直接把本地方法栈和虚拟机栈合二为一。 (所以说规范和实现要分开学习)

方法区,永久代,元空间(MetaSpace)的区别

总的来说, 方法区是接口, 永久代和元空间是实现

在 HotSpot 中,1.7 及以前的版本以永久代做为方法区的实现, 1.8 及以后版本的 jdk 以 MetaSpace 做方法区的实现。

永久代在堆里面, MetaSpace 直接使用了直接(本地)内存。

相应的 1.8 及以后。 移除了永久代,以 MetaSpace 做方法区实现,常量池中的字符串常量池,直接留在了堆中。 其他的如类信息,静态信息留在了 MetaSpace 中,也跟着去了直接(本地)内存

总结

本文以 JVM 规范为主,部分区域给出了 HotSpot 的实现,先学规范,再学实现,两者切记一定要分开学,不然就学着学着就混乱了!

  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...