乐趣区

对于JVM你就只知道堆和栈吗

不少 java 程序员一提 JVM 运行时数据区域,就会说堆和栈,当然也有 java 程序员给出方法区、虚拟机栈、本地方法栈、堆、程序计数器这个答案,但是还有人给出永久代、虚拟机栈、本地方法栈、堆、程序计数器这个答案。那么究竟哪种答案是正确的呢?

首先我们介绍两个概念,这对于我们回答上面的问题来说是必不可少的。
1. 虚拟机: 简单的说虚拟机是一种抽象的计算机。
2.Java 虚拟机规范: 虚拟机规是一种对实现 java 虚拟机的要求规范,Java 虚拟机负责对虚拟机规范进行实现。
我们平常所说 JAVA 虚拟机一般是指的一种具体的 JAVA 虚拟机规范的实现,比如说 HotSpot。当然市面上还有其他的 Java 虚拟机。
下面的截图内容来自 https://docs.oracle.com/javas…

从 JVM6 规范, 我们可以看到,规范要求的运行时数据区域有: PC Register(程序计数器)、Java Virtual Machine Stacks(java 虚拟机栈)、Heap(堆)、Method Area(方法区)、Native Method Stacks(本地方法栈)、Runtime Constant Pool(运行时常量池这及部分)。
注意标红部分, 我们可以看到运行时常量池位于方法区内。
根据规范我们可以大概的画出 JVM 运行实数据区域 (执行引擎、本地库接口在后面的文章中会进行详细介绍)。

在认真的看完 JVM6、7、8 的运行时数据区域规范之后,JVM6、7、8 关于对运行时的数据区域要求一点变化都没有,还是程序计数器、Java 虚拟栈、堆、方法区、本地方法栈、运行时常量池这几个部分。
那永久代是方法区的别名咯,准确的说是方法区的实现。规范是这么要求的,但是虚拟机会对这些区域进行调整,或者说是调整对 JVM 规范的实现。
查找了很多资料以后,还是没有找到官方对于永久代的描述 (有找到官方文档关于永久代(Permanent Generation) 的描述的,请联系我)。综合了很多博客和一些资料之后, 我们可以做出如下判断,永久代是 HotSpot 虚拟机特有的概念,HotSpot 团队使用永久代来实现 JVM 规范中的方法区。对于其他虚拟机来说是不存在永久代这个概念的。jdk8 移除了永久代,关于这点我们将在后文进行详细的介绍。
那么为什么称方法区为永久代呢?
这跟 GC 分代收集有一定的关系,内存回收发生在方法区和堆中。
我们使用 -XX:+PrintGCDetails 参数, 在 IDEA 中查看堆中的信息。

注意到 PSYoungGen 和 ParOldGen 这两个单词,从控制台的信息我们可以看出堆中的区域划分
PSYoungGen

eden space
from space
to    space

ParOldGen
PS 和 Par 代表垃圾回收器
也就是说堆里面又可以再分为年轻代、老年代,堆是垃圾回收机制的重点区域,将方法区也纳入垃圾回收范围,与年轻代、老年代相对,称方法区为永久代。
这就是永久代的来历。
接下来介绍一下 JVM1.7 和 1.6 的不同。
在介绍 JVM1.6 和 1.7 的区别之前,我们先介绍 String 类的 intern()方法
字符串在调用该方法的时候,如果字符串常量池里面还没有该字符串,则将该字符串添加进字符串常量池中。
查了一下,还是没有查到 HotSpot 官方对于运行时数据区域的说明,但是许多博客都指出 jdk1.7 将字符串常量池移动到了堆中。
以下是分别在 1.6、1.8 的测试例子

1.6 结果: false
1.7 以上的结果:true
我们来解释一下原因,1.6 中字符串常量池放在永久代中和堆是隔离的。
我们来画个图来解释一下,
1.6

1.7 的虚拟机做了调整,常量池中不需要在存储一份对象了,可以存储堆中的引用,更为灵活。
1.7 及以上的图

请看下一张图。
jdk 版本在 1.8。MetaSpace 是什么鬼?

Jdk1.8 移除了永久代或者说用 MetaSpace 去实现虚拟机规范中的方法区。
详见:JEP 122: Remove the Permanent Generation
我查了很多资料,还是没有找到官方对 MetaSpace 的解释和描述,到是在深入探究 JVM | 探秘 Metaspace 这篇博客中看到了 MetaSpace 的相关描述。

In JDK 8, classes metadata is now stored in the native heap and this space is called Metaspace.

字符串常量池仍然在堆中,方法区移动至 MetaSpace 中
那么如果有人问你 JVM 运行时区域由几部分组成,那么应该如何回答呢?
我认为答案可以是这样的:JVM 规范中的要求 JVM 运行时区域有以下及部分:

  1. 方法区
  2. java 虚拟机栈
  3. 本地方法栈
  4. 程序计数器
  5. 运行时常量池(位于方法区中)

但是不同的虚拟机对规范有着不同的实现,HotSpot1.6 对方法区的实现叫永久代,1.7 将字符串常量池移动到了堆中,1.8 移除了永久代改用元数据区实现方法区,字符串常量池仍然在堆中。

退出移动版