[转帖]方法区中到底是什么样的

方法区中到底是什么样的

markword+class metadata address+【数组长度】+对齐填充

其中class metadata address,即类的元数据就是存储在方法区(1.8之后在元空间)的地址,那么这个类的元数据到底是什么样的?

R大的一篇博客中我找到了答案:

这是R大通过地址找到的方法去中类元数据的样子,InstanceKlass存着Java类型的名字、继承关系、实现接口关系,字段信息,方法信息,运行时常量池的指针,还有内嵌的虚方法表(vtable)、接口方法表(itable)和记录对象里什么位置上有GC会关心的指针(oop map)等等。

留意到这个InstanceKlass是给VM内部用的,并不直接暴露给Java层;InstanceKlass不是java.lang.Class的实例。

在HotSpot VM里,java.lang.Class的实例被称为“Java mirror”,意思是它是VM内部用的klass对象的“镜像”,把klass对象包装了一层来暴露给Java层使用。

在InstanceKlass里有个_java_mirror字段引用着它对应的Java mirror,而mirror里也有个隐藏字段指向其对应的InstanceKlass。

所以当我们写obj.getClass(),在HotSpot VM里实际上经过了两层间接引用才能找到最终的Class对象:

obj->_klass->_java_mirror

在Oracle JDK7之前,Oracle/Sun JDK的HotSpot VM把Java类的静态变量存在InstanceKlass结构的末尾;从Oracle JDK7开始,为了配合PermGen移除的工作,Java类的静态变量被挪到Java mirror(Class对象)的末尾了。

还有就是,在JDK7之前Java mirror存放在PermGen里,而从JDK7开始Java mirror默认也跟普通Java对象一样先从eden开始分配而不放在PermGen里。到JDK8则进一步彻底移除了PermGen,把诸如klass之类的元数据都挪到GC堆之外管理,而Java mirror的处理则跟JDK7一样。

 

HotSpot VM里有一套对象专门用来存放元数据,它们包括:

  • Klass系对象。元数据的最主要入口。用于描述类型的总体信息
  • ConstantPool/ConstantPoolCache对象。每个InstanceKlass关联着一个ConstantPool,作为该类型的运行时常量池。这个常量池的结构跟Class文件里的常量池基本上是对应的。可以参考我以前的一个回帖。ConstantPoolCache主要用于存储某些字节码指令所需的解析(resolve)好的常量项,例如给[get|put]static、[get|put]field、invoke[static|special|virtual|interface|dynamic]等指令对应的常量池项用。
  • Method对象,用来描述Java方法的总体信息,像是方法入口地址、调用/循环计数器等等
  • ConstMethod对象,记录着Java方法的不变的描述信息,包括方法名、方法的访问修饰符、字节码、行号表、局部变量表等等。注意了,字节码就嵌在这ConstMethod对象里面。
  • Symbol对象,对应Class文件常量池里的JVM_CONSTANT_Utf8类型的常量。有一个VM全局的SymbolTable管理着所有Symbol。Symbol由所有Java类所共享。
  • MethodData对象,记录着Java方法执行时的profile信息,例到过null,某个条件如某方法里的某个字节码之类是否从来没遇跳转是否总是走同一个分支,等等。这些信息在解释器(多层编译模式下也在低层的编译生成的代码里)收集,然后供给HotSpot Server Compiler用于做激进优化。

 

 

在PermGen移除前,上述元数据对象都在PermGen里,直接被GC管理着。

JDK8彻底移除PermGen后,这些对象被挪到GC堆外的一块叫做Metaspace的空间里做特殊管理,仍然间接的受GC管理。

猜你喜欢

转载自www.cnblogs.com/jinanxiaolaohu/p/12890885.html
今日推荐