史上最详细java对象占几个字节?

一、下载工具

要想分析某个对象大小,要借助一个工具jol-core,他的maven地址是:

<dependency>
  <groupId>org.openjdk.jol</groupId>
  <artifactId>jol-core</artifactId>
  <version>0.9</version>
</dependency>

二、测试

首先新建一个空类,里面不包含任何字段。然后使用jol-core打印一下对象布局。

public class Dog {
}

public class App 
{

    public static void main( String[] args )
    {
        Dog dog =new Dog();
        System.out.println(ClassLayout.parseInstance(dog).toPrintable());
    }
}

他将输出如下信息,在最后,有一条信息为"Instance size: 16 bytes",也就是说这个对象是16个字节。

 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)                           44 c1 00 f8 (01000100 11000001 00000000 11111000) (-134168252)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes

三、对象内存布局

要说这16个字节哪来的,就得先了解java对象的内存布局,也就是如下图。
对象的内存布局分为对象头、对象中实际数据、对其填充。
先不讨论这三个干什么用,只需要知道他们分别占几个字节就行了,毕竟本文只说对象大小。
在这里插入图片描述

3.1 对象头大小

第一部分是对象的脑袋,分为Mark Word和Class Pointer,在不同位数的JVM有不同的大小。
在32位系统上,Mark Word是4字节,Class Pointer也是4字节,总共是8字节。
在64位系统上,受指针压缩影响,开启指针压缩时Mark Word是8字节,Class Pointer是4字节,总共12字节。关闭指针压缩Mark Word是8字节,Class Pointer也是8字节。

3.2 指针压缩

在64位上有一个指针压缩的概念,参数为XX:+UseCompressedOops,默认是开启的,开启后Class Pointer将被压缩为4字节,对象头最终共为12个字节。

64位JVM测试
开启指针压缩时输出(默认就是开启),不用管,最终大小为16字节。

 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)                           44 c1 00 f8 (01000100 11000001 00000000 11111000) (-134168252)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

接着测试关闭指针压缩,在IDEA中点击运行配置,VM options中输入-XX:-UseCompressedOops,代表关闭指针压缩。
在这里插入图片描述
结果输出如下

 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)                           e0 e4 e0 94 (11100000 11100100 11100000 10010100) (-1797200672)
     12     4        (object header)                           07 7f 00 00 (00000111 01111111 00000000 00000000) (32519)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

你会发现最终大小都是16字节,但是开启压缩时有一行 (loss due to the next object alignment)大小为4字节,这个表示对齐填充,这是因为人家设计时就要求大小为8的倍数,原本12不是8的倍数,所以自己又填充了4。对齐填充没有其他意义,只为了数据对齐,保证是8的倍数。

32位JVM测试
需要下载一个32为jdk。然后快捷键Ctrl+Alt+Shift+S打开项目配置,选择32位的jdk路径。 在这里插入图片描述
结果为8个字节,也不需要数据对其。

com.hxl.entity.Dog object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           e0 c5 cd a3 (11100000 11000101 11001101 10100011) (-1546795552)
Instance size: 8 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

3.3 实际数据

在Dog中增加一个整形和cha类型的字段。

public class Dog {
    private int age;
    private char date;
}

64位JVM测试
此时在开启指针压缩情况下输出:

 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)                           44 c1 00 f8 (01000100 11000001 00000000 11111000) (-134168252)
     12     4    int Dog.age                                   0
     16     2   char Dog.date                                   
     18     6        (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 6 bytes external = 6 bytes total

因为int占4字节,char占2个字节,所以,原本的大小为12(对象头)+4(int)+2(char)=18,但是18不是8的倍数啊,所以要进行数据对齐,填了6个,18+6=24个字节。

在关闭指针压缩情况下输出:

 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)                           10 a5 23 b7 (00010000 10100101 00100011 10110111) (-1222400752)
     12     4        (object header)                           22 7f 00 00 (00100010 01111111 00000000 00000000) (32546)
     16     4    int Dog.age                                   0
     20     2   char Dog.date                                   
     22     2        (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 2 bytes external = 2 bytes total

按照上面所说,原本大小为16(对象头大小)+4(int)+2(char)=22,还需填2个字节就是8的倍数了,所以上面输出(loss due to the next object alignment)是2个字节。

32位JVM测试
但是这一切的一切,在32位的JVM上输出如下,只有16个字节。
32位没有指针压缩的概念,所以原本大小为8(对象头)+4(int)+2(char)=14,还要填充2字节为8的倍数。所以最终为16字节。

 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           08 c6 cd a3 (00001000 11000110 11001101 10100011) (-1546795512)
      8     4    int Dog.age                                   0
     12     2   char Dog.date                                   
     14     2        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 2 bytes external = 2 bytes total

3.4 引用大小

在dog中增加name。

public class Dog {
    private String name;
}

64位JVM测试
同样在关闭指针压缩时输出如下,发现引用类型占8字节。最终大小为24字节。

 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)                           f8 34 a0 2f (11111000 00110100 10100000 00101111) (799028472)
     12     4                    (object header)                           3c 7f 00 00 (00111100 01111111 00000000 00000000) (32572)
     16     8   java.lang.String Dog.name                                  null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

开启指针压缩则不同了。引用类型占4字节。最终大小为16字节,也就是把引用类型压缩成4字节了。

 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)                           44 c1 00 f8 (01000100 11000001 00000000 11111000) (-134168252)
     12     4   java.lang.String Dog.name                                  null
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

32JVM测试
32为上对象头8字节,引用类型占4字节。总共加上对其填充为16字节。

com.hxl.entity.Dog object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0     4                    (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                    (object header)                           f8 c5 dd a3 (11111000 11000101 11011101 10100011) (-1545746952)
      8     4   java.lang.String Dog.name                                  null
     12     4                    (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

3.5 数组类型

但是如果对象是数组,在对象头中多出一条表示数组的长度。如下图
在这里插入图片描述
如下代码。

        Dog[] dogs ={new Dog()};
        System.out.println(ClassLayout.parseInstance(dogs).toPrintable());

红色地方为数组的大小。
在这里插入图片描述
数组分两种,基本数据类型数组和引用数据类型数组,基本数据类型数组计算方式为 (数组个数x单个元素大小),比如int数组,每个int是4字节,如果有5个元素,则数组占20字节。

但是引用类型就不一样了,开启指针压缩情况下为(数组长度x4),每个引用4字节,关闭后,每个引用8字节,占用(数组长度x8)字节。

如下,数组长度为8,每个引用8字节,最终占64字节(关闭指针压缩)。

Dog[] dogs ={new Dog(),new Dog(),new Dog(),new Dog(),new Dog(),new Dog(),new Dog(),new Dog()};
System.out.println(ClassLayout.parseInstance(dogs).toPrintable());
[Lcom.hxl.entity.Dog; 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)                           20 37 a0 93 (00100000 00110111 10100000 10010011) (-1818216672)
     12     4                      (object header)                           11 7f 00 00 (00010001 01111111 00000000 00000000) (32529)
     16     4                      (object header)                           08 00 00 00 (00001000 00000000 00000000 00000000) (8)
     20     4                      (alignment/padding gap)                  
     24    64   com.hxl.entity.Dog Dog;.<elements>                           N/A
Instance size: 88 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

在32位上则(数组个数x4)。如8个引用元素则是32字节。

发布了42 篇原创文章 · 获赞 7 · 访问量 7750

猜你喜欢

转载自blog.csdn.net/HouXinLin_CSDN/article/details/104222728
今日推荐