简介
在之前的文章中,咱们介绍了应用JOL这一神器来解析java类或者java实例在内存中占用的空间地址。
明天,咱们会更进一步,分析一下在之前文章中没有解说到的更深层次的细节。一起来看看吧。
对象和其暗藏的机密
java.lang.Object大家应该都很相熟了,Object是java中所有对象的鼻祖。
接下来咱们来对这个java对象的鼻祖进行一个具体的解剖剖析,从而了解JVM的深层次的机密。
工具当然是应用JOL:
@Slf4jpublic class JolUsage { @Test public void useJol(){ log.info("{}", VM.current().details()); log.info("{}", ClassLayout.parseClass(Object.class).toPrintable()); log.info("{}", ClassLayout.parseInstance(new Object()).toPrintable()); }}
代码很简略,咱们打印JVM的信息,Object class和一个新的Object实例的信息。
看下输入:
[main] INFO com.flydean.JolUsage - # Running 64-bit HotSpot VM.# Using compressed oop with 3-bit shift.# Using compressed klass with 3-bit shift.# WARNING | Compressed references base/shifts are guessed by the experiment!# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.# Objects are 8 bytes aligned.# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]10:27:32.311 [main] INFO com.flydean.JolUsage - java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 (loss due to the next object alignment)Instance size: 16 bytesSpace losses: 0 bytes internal + 4 bytes external = 4 bytes total10:27:32.312 [main] INFO com.flydean.JolUsage - java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 86 06 00 00 (10000110 00000110 00000000 00000000) (1670) 12 4 (loss due to the next object alignment)Instance size: 16 bytesSpace losses: 0 bytes internal + 4 bytes external = 4 bytes total
从下面的后果咱们晓得,在64位的JVM中,一个Object实例是占用16个字节。
因为Object对象中并没有其余对象的援用,所以咱们看到Object对象只有一个12字节的对象头。剩下的4个字节是填充位。
Object对象头
那么这12字节的对象头是做什么用的呢?
如果想要深刻理解这12字节的对象头,当然是要去研读一下JVM的源码:src/share/vm/oops/markOop.hpp。
有趣味的小伙伴能够去看看。如果没有趣味,没关系,这里给大家一个张总结的图:
javaObject对象的对象头大小依据你应用的是32位还是64位的虚拟机的不同,稍有变动。这里咱们应用的是64位的虚拟机为例。
Object的对象头,分为两局部,第一局部是Mark Word,用来存储对象的运行时数据比方:hashcode,GC分代年龄,锁状态,持有锁信息,偏差锁的thread ID等等。
在64位的虚拟机中,Mark Word是64bits,如果是在32位的虚拟机中Mark Word是32bits。
第二局部就是Klass Word,Klass Word是一个类型指针,指向class的元数据,JVM通过Klass Word来判断该对象是哪个class的实例。
且慢!
有的小伙伴可能发现了问题,之前咱们用JOL解析Object对象的时候,Object head大小是12字节,也就是96bits,这里怎么写的是128bits?
没错,如果没有开启COOPs就是128bits,如果开启了COOPs,那么Klass Word的大小就从64bits降到了32bits。
还记得咱们之前讲的COOPs吗?
COOPs就是压缩对象指针技术。
对象指针用来指向一个对象,示意对该对象的援用。通常来说在64位机子下面,一个指针占用64位,也就是8个字节。而在32位机子下面,一个指针占用32位,也就是4个字节。
实时上,在应用程序中,这种对象的指针是十分十分多的,从而导致如果同样一个程序,在32位机子下面运行和在64位机子下面运行占用的内存是齐全不同的。64位机子内存应用可能是32位机子的1.5倍。
而压缩对象指针,就是指把64位的指针压缩到32位。
怎么压缩呢?64位机子的对象地址依然是64位的。压缩过的32位存的只是绝对于heap base address的位移。
咱们应用64位的heap base地址+ 32位的地址位移量,就失去了理论的64位heap地址。
对象指针压缩在Java SE 6u23 默认开启。在此之前,能够应用-XX:+UseCompressedOops来开启。
数组对象头
java中有一个十分特地的对象叫做数组,数组的对象头和Object有什么区别吗?
咱们用JOL再看一次:
log.info("{}",ClassLayout.parseClass(byte[].class).toPrintable());log.info("{}",ClassLayout.parseInstance("www.flydean.com".getBytes()).toPrintable());
下面的例子中咱们别离解析了byte数组的class和byte数组的实例:
10:27:32.396 [main] INFO com.flydean.JolUsage - [B object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 16 (object header) N/A 16 0 byte [B.<elements> N/AInstance size: 16 bytesSpace losses: 0 bytes internal + 0 bytes external = 0 bytes total10:27:32.404 [main] INFO com.flydean.JolUsage - [B object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 22 13 07 00 (00100010 00010011 00000111 00000000) (463650) 12 4 (object header) 0f 00 00 00 (00001111 00000000 00000000 00000000) (15) 16 15 byte [B.<elements> N/A 31 1 (loss due to the next object alignment)Instance size: 32 bytesSpace losses: 0 bytes internal + 1 bytes external = 1 bytes total
看到区别了吗?咱们发现数组的对象头是16字节,比一般对象的对象头多出了4个字节。这4个字节就是数组的长度。
整个对象的构造
好了,写到这里咱们来总结一下,java对象的构造能够分为一般java对象和数组对象两种:
数组对象在对象头中多了一个4字节的长度字段。
大家看到最初的字节是padding填充字节,为什么要填充呢?
因为JVM是以8字节为单位进行对其的,如果不是8字节的整数倍,则须要补全。
本文链接:http://www.flydean.com/jvm-java-object-in-heap/最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」,懂技术,更懂你!