你还在看《深入理解Java虚拟机》的运行时数据模型吗?

Posted 爪哇技术精选

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你还在看《深入理解Java虚拟机》的运行时数据模型吗?相关的知识,希望对你有一定的参考价值。

学习JVM必看的书籍无疑是《深入理解Java虚拟机》这本书了,在书中,关于运行时数据区域模型是这样描述的:

在这里我们只针对HotSpot VM来说,它是OracleJDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机。在JDK7之前,这样的模型是正确的。但是到了JDK8,如图标红的部分,做了一些优化。

什么是方法区,什么是永久代,运行时常量池又是什么

  • “方法区”(Method Area),是线程共享的区域,用于存储已被虚拟机加载的类信息,常量,静态变量等数据。首先我们要知道,方法区是JVM的一种规范,是一个概念,而这个方法区的具体实现由各个虚拟机厂商去实现。

  • “永久代”(Permanent Generation)就是HotSpot虚拟机对于方法区的实现,也仅仅是针HotSpot才有的。

  • “运行时常量池”是方法区的一部分。用于存放编译期生成的各种字面量和符号引用。其特性是具备动态性。

优化一:字符串常量池从永久代划到Java堆

由于常量池具备动态性,在程序运行过程中会有大量的字符串常量在运行时常量池里产生,此时如果放在永久代,则无法恰当的设定永久代的大小,容易出现性能问题和内存溢出。下面一个例子证明在JDK8中,字符串常量池已经放在堆中:

String.intern()方法的作用是返回一个字符串引用,引用的是字符串常量池中的字符串(字面量),我们先来验证一下这个方法:

 
   
   
 
  1. public class StringConstantsPoolTest {

  2. public static void main(String[] args) {

  3. String str = "abc"; // str存储在常量池

  4. String str2 = new String("abc"); // str2 存储在堆中

  5. System.out.println(str == str2); // 结果为false ,堆中的引用并不等于常量池中的引用

  6. str2 = str2.intern(); // 获取str2在常量池中的引用

  7. System.out.println(str == str2);

  8. }

  9. }

结果如下:你还在看《深入理解Java虚拟机》的运行时数据模型吗?

证明 String.intern()方法返回了一个在常量池中的引用。 下面验证字符串常量池在堆中:

 
   
   
 
  1. public static void main(String[] args) {

  2. ArrayList<String> list = new ArrayList<>();

  3. for (int i = 0; i < 100000000; i++) {

  4. for (int j = 0; j < 1000000; j++) {

  5. list.add(String.valueOf(i + j / 1000000).intern());

  6. }

  7. }

  8. }

结果如下:

 我们看到这时报的是Java堆空间内存溢出,说明字符串常量池是在堆中,注意,此时仅仅是字符串常量池转移到了堆中,但是运行时常量池依旧还是在方法区里

优化二:移除了永久代,引入“元空间”(Metaspace)

为什么移除永久代?

  1. 方法区大小难以设定,容易发生内存溢出。永久代会存放Class的相关信息,一般这些信息在编译期间就能确定大小。但是如果是在一些需要动态生成大量Class的应用中,如:Spring的动态代理、大量的JSP页面或动态生成JSP页面等,由于方法区的大小在一开始就要分配好,因此就能难确定大小,容易出现内存溢出

  2. GC复杂且效率低。方法区存储了类的元数据信息和各种常量,它的内存回收目标理应当是对这些类型的卸载和常量的回收。但由于这些数据被类的实例引用,卸载条件变得复杂且严格,回收不当会导致堆中的类实例失去元数据信息和常量信息。因此,回收方法区内存不是一件简单高效的事情。

  3. 促进HotSpot JVM与JRockit VM的融合。JRockit没有方法区,移除永久代可以促进HotSpot JVM与JRockit VM的融合。

什么是元空间(Metaspace),为什么引入元空间

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。

元空间的特点:

  1. 每个加载器有专门的存储空间。

  2. 不会单独回收某个类。

  3. 元空间里的对象的位置是固定的。

  4. 如果发现某个加载器不再存活了,会把相关的空间整个回收。

总结

最终JVM(HotSpot)运行时数据区域模型如下:


以上是关于你还在看《深入理解Java虚拟机》的运行时数据模型吗?的主要内容,如果未能解决你的问题,请参考以下文章

深入理解JVM虚拟机读书笔记——内存模型与线程

深入理解JVM虚拟机读书笔记——内存模型与线程

深入理解Java虚拟机:运行时数据区域

深入理解Java虚拟机

深入理解java虚拟机HotSpot虚拟机对象解析

深入理解java虚拟机HotSpot虚拟机对象解析