关于Java方法区的一些问题

前言

以下关于JVM方法区的一些相关问题。


我们来看一下方法区的概念:

与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态信息、即时编译期编译后的代码。

那么在JDK1.6中,永久代和方法区的关系。

对于习惯在HotSpot VM上开发、部署程序的开发者来说,很多人都更愿意把方法区称为“永久代”,本质上两者是不等价的,方法区和永久代有着本质的区别,前者是JVM的规范,而后者则是JVM规范的一种实现,并且只有 HotSpot 才有 “永久代”,而对于其他类型的虚拟机,如 JRockit(Oracle)、J9(IBM) 并没有“永久代”。

那么方法区所存储的数据都是放在永久代吗?

并不是,按JVM规范对方法区的定义,JIT编译生成的代码也应该属于方法区的一部分,但它在HotSpot里并不放在GC heap里而是放在不直接由GC收集的CodeCache区域里,这个确实在native memory。

何为native memory?

对HotSpot VM来说,不受GC管理的内存都是native memory;受GC管理的内存叫做GC heap或者managed heap.

我们来看一下方法区在不同版本的一些变化吧:

我们都知道,JDK1.8已经移除了永久代,其实在JDK1.7版本已经开始执行去永久代的工作了。JDK1.7中,存储在永久代的部分数据已经转移到了Java Heap或者Native Memory中。

例如:

  1. Oracle JDK7 / OpenJDK 7的HotSpot VM把符号引用(Symbols)的存储从PermGen移动到了native memory中。
  2. 静态变量从instanceKlass末尾(位于PermGen内)移动到了java.lang.Class对象的末尾(位于普通Java Heap)。
  3. 字面量(interned strings)转移到了java heap。

在周志明的深入Java虚拟机中,有这样一段话:在JDK1.7的HotSpot中,已经把原本放在永久代的字符串常量池移出。

注意常量池

  • 如果是runtime constant pool,那么它还是在永久代中的。
  • 如果是SymbolTable / StringTable,这两个table自身就一直在native memory里,只是他们所引用的东西存储位置发生了变化。JDK1.7把SymbolTable引用的Symbol从PermGen移动到了native memory,而StringTable引用的Java.lang.String实例从PermGen移动到了普通Java Heap。

最后一个问题:JVM 常量池中存储的是对象还是引用呢?

R大的回答是:如果您说的确实是runtime constant pool(而不是interned string pool / StringTable之类的其他东西)的话,其中的引用类型常量(例如CONSTANT_String、CONSTANT_Class、CONSTANT_MethodHandle、CONSTANT_MethodType之类)都存的是引用,实际的对象还是存在Java heap上的。

以上针对的是Runtime constant pool(运行时常量池),那么对于interned string pool / StringTable之类的就不一定咯。我们还是来看一下R大的解释。

用于管理interned String的那个string pool / StringTable,也只是存引用而不是存对象。

R大的解释:HotSpot VM的StringTable的本体在native memory里。它持有String对象的引用而不是String对象的本体。被引用的String还是在Java heap里。一直到JDK6,这些被intern的String在PermGen里,JDK7开始改为放在普通Java heap里。

再结合R大对堆的解释:

JVM规范对抽象的“Java heap”的定义是“存储Java对象的地方”,也就是说Java对象在哪里,哪里就是Java heap。HotSpot的PermGen里是会存储部分Java对象的,例如说一些java.lang.String实例。这些String实例占的部分就得算是Java heap。

总结: =。=md… 并没有总结…

参考:

  • 深入理解Java虚拟机
  • R大关于方法区的Class信息,又称为永久代,是否属于Java堆?的回复
  • java 8中撤销永久代,引入元空间

你可能感兴趣的:(JVM系列)