Java利用JOL工具分析对象分布

对象的组成

在这里插入图片描述

对象头[Header]

  1. Markword:存储对象自身运行时数据如hashcode、gc分代年龄等,64位系统总共占用8个字节,关于Markword详细内存分布如下
    在这里插入图片描述
  2. 类型指针:对象指向类元数据地址的指针,jdk8默认开启指针压缩,64位系统占4个字节
  3. 数组长度:若对象不是数组,则不分配空间大小,若是数组,则为4个字节长度

基于上面描述数组和普通对象的对象头有不同的内存大小,主要区别在于数组长度
在这里插入图片描述

实例数据[Instance Data]

指的就是对象中各个属性大小,比如User中name和age的内存大小

public class User{
   String name;
   int age;
}

若开启了类型指针压缩,String是4个字节, int是4个字节,属性填充总共8个字节

内存对齐[Padding]

因为JVM要求java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8字节的倍数,就不特别介绍了

JOL工具分析对象

Java项目引入依赖

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

创建对象与结果分析

创建简单无锁对象

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

输出结果分析

java.lang.Object 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)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

OFFSET代表内存偏移量,单位是字节
SIZE代表占用内存大小,单位字节
TYPE DESCRIPTION 类型描述,其中​​object header​​为对象头
VALUE代表存储值,对应内存中当前存储的值;

对象总大小为16个字节,对象头占用12个字节,前8个字节代表markword,对象运行时数据状态,前64位的value倒序排列拼的markword是16进制为00 00 00 00 00 00 00 01,最后一个字节01的二进制是00000001,最后三位001,代表无锁状态,后4个字节代表对象指向类元数据的指针;空对象没有属性所以实例数据不占内存,对象头+实例数据=12,不是8个倍数,所以补4个字节为16字节

enum {  locked_value              = 0, // 0 00 轻量级锁
         unlocked_value           = 1,// 0 01 无锁
         monitor_value            = 2,// 0 10 重量级锁
         marked_value             = 3,// 0 11 gc标志
         biased_lock_pattern      = 5 // 1 01 偏向锁
  };

创建有属性的对象

public class ClassLayOutCheck {
    public static void main(String[] args) {
        ClassLayout classLayout = ClassLayout.parseInstance(new User());
        System.out.println(classLayout.toPrintable());

    public static class User {
        String name;
        int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
}

输出结果分析

org.example.object.ClassLayOutCheck$User 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)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4                int User.age                                  1
     16     4   java.lang.String User.name                                 (object)
     20     4                    (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

User对象总大小为24个字节,对象头占用12个字节,前8个字节代表markword,对象运行时数据状态,前8个字节低3位001代表无锁状,后4个字节代表对象指向类元数据的指针;User实例数据是8个字节,有属性name和age,其中name是String类型,不过这里有个问题,显示string的字节长度是4,value显示object,我们都知道内部实现是byte[],其中4字节指代对象内存指针,age是int类型,默认4个字节,value直接显示值;对象头+实例数据=20不是8字节倍数,所以填充4个字节为24个字节

创建数组

public static void main(String[] args) {
        ClassLayout classLayoutInt = ClassLayout.parseInstance(new int[]{});
        System.out.println("----------------------");
        System.out.println(classLayoutInt.toPrintable());
    }
[I 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)                           6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363)
     12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     0    int [I.<elements>                             N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

结果输出分析

对象总大小为16个字节,对象头占用16个字节,前8个字节代表markword,对象运行时数据状态,低2位00代表轻量级锁,其他位是指向栈针中锁记录的指针,4个字节代表对象指向类元数据的指针,最后4字节代表数字的长度;空数组没有属性所以实例数据不占内存,对象头+实例数据=16,是8字节的倍数,不需要填充数据

创建重量级锁对象

public static void main(String[] args) {
        User l = new User("cyl",1);
        Runnable runnable = () -> {

            synchronized (l) {

                ClassLayout layout = ClassLayout.parseInstance(l);
                System.out.println(layout.toPrintable());
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        };

        for (int i = 0; i < 3; i++) {
            new Thread(runnable).start();
        }

    }


    public static class User {
        String name;
        Integer age;

        User(String name, Integer age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }


        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
    ```
    
```java
org.example.object.ClassLayOutCheckThread$User object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           5a 1d 04 43 (01011010 00011101 00000100 01000011) (1124343130)
      4     4                     (object header)                           ab 7f 00 00 (10101011 01111111 00000000 00000000) (32683)
      8     4                     (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4    java.lang.String User.name                                 (object)
     16     4   java.lang.Integer User.age                                  1
     20     4                     (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

org.example.object.ClassLayOutCheckThread$User object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           5a 1d 04 43 (01011010 00011101 00000100 01000011) (1124343130)
      4     4                     (object header)                           ab 7f 00 00 (10101011 01111111 00000000 00000000) (32683)
      8     4                     (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4    java.lang.String User.name                                 (object)
     16     4   java.lang.Integer User.age                                  1
     20     4                     (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

org.example.object.ClassLayOutCheckThread$User object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           5a 1d 04 43 (01011010 00011101 00000100 01000011) (1124343130)
      4     4                     (object header)                           ab 7f 00 00 (10101011 01111111 00000000 00000000) (32683)
      8     4                     (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4    java.lang.String User.name                                 (object)
     16     4   java.lang.Integer User.age                                  1
     20     4                     (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

header倒叙最后一个字节是5a,对应二进制是01011010,查看最后三位010,可知对象状态是重量级锁

创建轻量级锁对象

public static void main(String[] args) {
        User l = new User("cyl",1);
        Runnable runnable = () -> {

            synchronized (l) {

                ClassLayout layout = ClassLayout.parseInstance(l);
                System.out.println(layout.toPrintable());
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        };

        for (int i = 0; i < 1; i++) {
            new Thread(runnable).start();
        }

    }


    public static class User {
        String name;
        Integer age;

        User(String name, Integer age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }


        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

输出结果分析

org.example.object.ClassLayOutCheckThread$User object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           00 c9 3f 09 (00000000 11001001 00111111 00001001) (155175168)
      4     4                     (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
      8     4                     (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4    java.lang.String User.name                                 (object)
     16     4   java.lang.Integer User.age                                  1
     20     4                     (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

header倒叙最后一个字节是00,对应二进制是00000000,查看最后三位000,可知对象状态是轻量级锁

局限性

当对象中嵌套对象时,得出Instance size这是当前对象内存大小,不是所有内存大小,比如set对象,实例属性是map,4个字节表示的对象的引用指针,而不是内部map的实际大小

java.util.HashSet 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)                           1c 72 00 f8 (00011100 01110010 00000000 11111000) (-134188516)
     12     4   java.util.HashMap HashSet.map                               (object)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

可以清楚的看到实例属性java.util.HashMap HashSet.map 的value
是object,4个字节代表它的引用指针。查询实际Java对象中嵌套对象的实际内存大小,可以自己写工具,主要就是递归直到事最基础数据类型,计算出来之后再累加,或者使用已经成熟的工具,参考博客:
两种工具查询Java嵌套对象引用内存大小

参考文章:

1.https://blog.csdn.net/superfjj/article/details/106582678
2.https://blog.51cto.com/u_15485936/5111767
3.https://blog.csdn.net/qq_38824137/article/details/107089862

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java对象内存模型包括对象、实例数据和对齐填充三部分。对象包含了关于堆对象的布局、类型、GC状态、同步状态和标识哈希码等基本信息。实例数据存放了类的数据信息、父类的信息以及对象字段属性信息。对齐填充主要是为了字节对齐而填充的数据,以凑齐8字节的倍数。 在JVM中,我们可以使用openjdk的jol工具来打印对象信息。通过调用`ClassLayout.parseInstance(object).toPrintable()`方法,我们可以打印出对象的信息。 例如,对于一个无属性的对象,打印的信息如下: ``` java.lang.Object 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) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total ``` 这段信息描述了对象的各个部分。例如,对象占据了前12个字节,分为三部分,每部分占4个字节。实例数据为空,所以没有具体的值。 这就是Java对象的内存模型。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Java对象内存模型](https://blog.csdn.net/u013190417/article/details/122532408)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值