欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

浅谈java object对象在heap中的结构

程序员文章站 2022-06-23 09:01:06
对象和其隐藏的秘密java.lang.object大家应该都很熟悉了,object是java中一切对象的鼻祖。接下来我们来对这个java对象的鼻祖进行一个详细的解剖分析,从而理解jvm的深层次的秘密。...

对象和其隐藏的秘密

java.lang.object大家应该都很熟悉了,object是java中一切对象的鼻祖。

接下来我们来对这个java对象的鼻祖进行一个详细的解剖分析,从而理解jvm的深层次的秘密。

工具当然是使用jol:

@slf4j
public 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 bytes

space losses: 0 bytes internal + 4 bytes external = 4 bytes total

10: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 bytes

space losses: 0 bytes internal + 4 bytes external = 4 bytes total3

从上面的结果我们知道,在64位的jvm中,一个object实例是占用16个字节。

因为object对象中并没有其他对象的引用,所以我们看到object对象只有一个12字节的对象头。剩下的4个字节是填充位。

object对象头

那么这12字节的对象头是做什么用的呢?

如果想要深入了解这12字节的对象头,当然是要去研读一下jvm的源码:src/share/vm/oops/markoop.hpp。

有兴趣的小伙伴可以去看看。如果没有兴趣,没关系,这里给大家一个张总结的图:

浅谈java object对象在heap中的结构

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?

浅谈java object对象在heap中的结构

没错,如果没有开启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/a

instance size: 16 bytes

space losses: 0 bytes internal + 0 bytes external = 0 bytes total

10: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 bytes

space losses: 0 bytes internal + 1 bytes external = 1 bytes total

看到区别了吗?我们发现数组的对象头是16字节,比普通对象的对象头多出了4个字节。这4个字节就是数组的长度。

整个对象的结构

好了,写到这里我们来总结一下,java对象的结构可以分为普通java对象和数组对象两种:

浅谈java object对象在heap中的结构

数组对象在对象头中多了一个4字节的长度字段。

大家看到最后的字节是padding填充字节,为什么要填充呢?

因为jvm是以8字节为单位进行对其的,如果不是8字节的整数倍,则需要补全。

以上就是浅谈java object对象在heap中的结构的详细内容,更多关于java object对象在heap中的结构的资料请关注其它相关文章!

相关标签: java object heap