klass模型是指Java类在JVM中的存在形式,用来存储类的元信息如:常量池、属性信息、方法信息……
看下klass模型类的继承结构
从继承关系上也能看出来,类的元信息是存储在原空间的
普通的Java类在JVM中对应的是instanceKlass类的实例,再来说下它的三个字类
- InstanceMirrorKlass:用于表示java.lang.Class,Java代码中获取到的Class对象,实际上就是这个C++类的实例,存储在堆区,学名镜像类
- InstanceRefKlass:用于表示java/lang/ref/Reference类的子类
- InstanceClassLoaderKlass:用于遍历某个加载器加载的类
Java中的数组不是静态数据类型,是动态数据类型,即是运行期生成的,Java数组的元信息用ArrayKlass的子类来表示:
- TypeArrayKlass:用于表示基本类型的数组
- ObjArrayKlass:用于表示引用类型的数组
下面我们通过工具来看一段Java类的instanceKlass信息
public class Test {
public static void main(String[] args) {
while (true);
}
}
执行main方法 通过 jps -l 命令行找到Test 的进程号并通过jdk自带的工具HSDB 来查看该类的instanceKlass元信息
我的进程号是 10056
D:\spring-boot\boot\target\classes>jps -l
23920 org.jetbrains.jps.cmdline.Launcher
10056 com.waf.boot.wk.Test
18076
18492 org.jetbrains.jps.cmdline.Launcher
23820 sun.jvm.hotspot.HSDB
25324 sun.tools.jps.Jps
通过cmd 运行 D:\java\jdk8\lib>java -cp .\sa-jdi.jar sun.jvm.hotspot.HSDB 命令打开HSDB工具 输入 进程号
找到 该对象的内存地址
通过内存地址查看instanceKlass 元信息
以上就是这个对象在jvm中的instanceKlass的元信息(_layout_helper:16 空对象,16B为对象大小,8字节对齐)
ArrayKlass
改下代码 在main方法里面加入两个数组,一个基础类型的数组,一个引用类型的数组,通过jclasslib来看看编译后的信息
public static void main(String[] args) {
int[] i_arr = new int[1];
String[] s_arr = new String[1];
while (true);
}
在main方法的字节码中有newarray 和 anewarray 两个数组 一个int(基本类型)一个String(引用类型)
JVM 字节码指令里面也有备注说明
当然也可以使用HSDB工具来查看 (注意是看main方法里面的)下面通过进程号开启HSDB工具
分别通过这两个内存地址查看instanceKlass 元信息
int[] 0x00000007160d43a8 String[] 0x00000007160d4398
oop(ordinary object pointer)
当Hotspot执行到这里时,会先将 ClassA 这个类型加载到 perm 区 ( 也叫方法区 ),然后在 Hotspot 堆中为其实例对象a开辟一块内存空间,存放实例数据。在 JVM加载ClassA到 perm 区时,JVM就会创建一个instanceKlass,instanceKlass中保存了 ClassA 这个 Java 类中所定义的一切信息,包括变量 、方法 、父类 、接 口、构造函数 、属性等,所以 instanceKlass 就是 ClassA这个Java类类型结构的对等体。而 instanceOop 这个“普通对象指针”对象中包含了一个指针,该指针就指向instanceKlass这个实例。在JVM实例化ClassA时,JVM又会在堆中创建一个instanceOop , instanceOop便是 ClassA 对象实例 a 在内存中的对等体,主要存储 ClassA 实例对象的成员变量。 其中,instanceOop 中有一个指针指向 instanceKlass ,通过这个指针,JVM便可以在运行期获取这个类实例对象的类元信息。
对象的内存布局
对象的内存大小,上面有提到过一个地方,16B的空对象。当然用HSDB也能看到,这边我们用jol-core包里面的api来看
openjdk 源码中 hotspot/src/share/vm/oops/markOop.hpp 这个文件也可以看到对象头里面的描述信息(32/64bit)
我们现在来看具体Java代码里面的(肯定是个16B的空对象)
对象在内存中就是这样布局的
Mark Word 32/64bit 4/8B
类型指针 Klass pointer
指针压缩 开启/关闭 4/8B
数组长度 如果这个对象不是数组/如果这个对象是数组,占0/4B
一个数组最多有多少元素
2的32次方 - 1
实例数据 类的非静态属性,生成对象时就是实例数据比如对象属性
如果是引用类型 指针压缩 开启/关闭 4/8B
对齐填充
Java中所有的对象大小都是8字节对齐 8的整数倍
如果一个对象占30B + JVM底层会补2B(对齐填充),凑成32字节,达到8字节对齐
64bit Test:8B(Mark Word)+4B(开启指针压缩)+0B(不是数组)+0B(没有属性)=12B+4B(对其填充)=16B
指针压缩属性 jdk1.6后默认开启的
普通对象
64bit Test:8B(Mark Word)+4B(开启指针压缩)+0B(不是数组)+4B^2(有属性)=20B+4B(对其填充)=24B
数组对象
64bit Test:8B(Mark Word)+4B(开启指针压缩)+4B(是数组)+4B^3(有属性)=28B+4B(对其填充)=32B