Klass的继承关系图
oopDesc的继承关系图
oop与oopDesc的关系图
JVM中,Klass代表一个Java类,oopDesc代表一个Java对象(其实只代表其头部信息),oop代表一个指向oopDesc的指针(即指向Java对象的指针)。
Klass的子类中,InstanceKlass代表一个普通的Java类(比如我们自定义的一个Java类),ArrayKlass代表数组类型的Java类(该Java类是JVM内部自动创建的,由数组维数和数组基础类型唯一确定)。
InstanceKlass的子类中,InstanceClassLoaderKlass代表的Java类是java.lang.ClassLoader,InstanceMirroKlass代表的Java类是java.lang.Class,InstanceRefKlass代表的Java类是java.lang.ref.Reference及其子类。由于JVM内部会对这三个Java类做特殊处理,所以内部也提取出了对应的InstanceKlass类。
ArrayKlass的子类中,TypeArrayKlass代表的是Java基础数据类型的一维数组所对应的类,比如int[].class、long[].class等,ObjArrayKlass代表的是对象类型的数组所对应的类,比如String[].class、Object[][].class等。这里需要特殊指出的是,int[]数组在Java内部其实也是一个Java对象,所以int[][].class对应的Klass是ObjArrayKlass,而不是TypeArrayKlass。
oopDesc的子类中,arrayOopDesc代表的是数组类型的对象,InstanceOopDesc代表的是普通Java类的对象,markOopDesc并不是代表一个Java对象,它只是因为历史原因被放到了这个继承关系里。
arrayOopDesc的子类中,objArrayOopDesc代表的是对象类型的数组,typeArrayOopDesc代表的是Java基础数据类型的一维数组。
当JVM加载或定义一个Java类时,它会在内部创建一个对应的Klass对象,用来存放该Java类的各种信息。而在该Klass对象创建过程中,它同时也会计算该Klass对象对应的Java类所创建的Java对象需要多大内存空间,该计算结果会被保存到Klass对象中的_layout_helper字段中,这样当运行时需要创建Java对象时,直接根据这个字段的值分配一块内存就好了。
InstanceKlass的计算规则大体上是 sizeof(instanceOopDesc) + 父类中非静态字段占用内存大小 + 该类中非静态字段占用内存大小。
ArrayKlass的计算规则大体上是 sizeof(arrayOopDesc) + sizeof(int) 用来存放数组长度 + 要分配的数组长度 * 数组元素所占内存大小。
InstanceMirrorKlass因为对应的Java类是java.lang.Class,所以它的计算规则很特殊,我们这里详细描述下。
首先,我们来想下创建java.lang.Class对象的用途是什么?
对,用来存放有关类的一些信息,比如类名等。
那什么时候需要创建它呢?
在类被加载或定义的时候,也就是在创建Klass对象的时候。
那既然已经有Klass对象了,为什么还要创建个java.lang.Class类型的Java对象呢?
因为Java对象有个getClass方法,需要返回其所属类对应的Java对象,这样可以在Java程序中获取Java类相关信息。
那这个java.lang.Class对象会有什么字段呢?
该对象会包含java.lang.Class类及其父类里所有非静态字段。
还包括其他字段吗?
还包括发起创建该对象对应的Java类中静态字段。
通过以上几个问答,现在我们可以比较清楚的知道,在Java类加载或定义过程中,会创建一个Klass对象,作为该Java类在JVM内部的代表,同时也会创建一个java.lang.Class类型的Java对象,作为该Java类在Java程序内的代表(该对象会被保存在Klass对象的_java_mirror字段中)。而这个java.lang.Class对象包含的字段为java.lang.Class类及其父类里所有非静态字段,加上发起创建该java.lang.Class对象对应的Java类中静态字段。
综上,InstanceMirrorKlass对象占用内存大小的计算规则大体上为:sizeof(instanceOopDesc) + java.lang.Class父类中非静态字段占用内存大小 + java.lang.Class类中非静态字段占用内存大小 + 发起创建该对象对应的Java类中静态字段占用内存大小。
创建普通Java对象对应的字节码为 new,对应的oop为instanceOop。
创建Java基础数据类型的一维数组对应的字节码为 newarray,对应的oop为typeArrayOop。
创建其他类型的一维数组或多维数组(该多维数组只有第一维指定了长度)对应的字节码为 anewarray,对应的oop为ObjArrayOop。
创建多维数组并且每一维都指定了具体长度对应的字节码为 multianewarray,对应的oop为ObjArrayOop。