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

news/2024/11/28 3:37:49/

文章目录

  • 对象的组成
    • 对象头[Header]
    • 实例数据[Instance Data]
    • 内存对齐[Padding]
  • JOL工具分析对象
    • Java项目引入依赖
    • 创建对象与结果分析
      • 创建简单无锁对象
      • 输出结果分析
      • 创建有属性的对象
      • 输出结果分析
      • 创建数组
      • 结果输出分析
      • 创建重量级锁对象
      • 输出结果分析
    • 局限性
  • 参考文章:

对象的组成

在这里插入图片描述

对象头[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                               VALUE0     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                               VALUE0     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                                  116     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                               VALUE0     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                               VALUE0     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                                  120     4                     (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalorg.example.object.ClassLayOutCheckThread$User object internals:OFFSET  SIZE                TYPE DESCRIPTION                               VALUE0     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                                  120     4                     (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalorg.example.object.ClassLayOutCheckThread$User object internals:OFFSET  SIZE                TYPE DESCRIPTION                               VALUE0     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                                  120     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                               VALUE0     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                                  120     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                               VALUE0     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


http://www.ppmy.cn/news/105194.html

相关文章

【数据分析之道-Numpy(八)】numpy统计函数

文章目录 专栏导读1、np.mean()2、np.median()3、np.std()4、np.var()5、np.min()6、np.max()7、np.sum()8、np.prod()9、np.percentile()10、np.any()11、np.all() 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN Python领域新星创作者&#xff0c;专注于分享python领…

用chatGPT来NEW个对象让“码农”的节日不再仅仅只有1024(赶鸭子上架式的成长、无效不得不立的flag)

用chatGPT来NEW个对象让“码农”的节日不再仅仅只有1024 前言一、大部分的成长都是赶鸭子上架二、节日是为了告诉自己不孤单三、做不到也要立下的flag四、New个对象吧1.php定义一个科技工作者形象2.python定义一个科技工作者形象3.javascript定义一个科技工作者形象 总结 前言 …

被黑客攻击了?无所谓,我会拔网线。。。

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 最近老是有粉丝问我&#xff0c;被黑客攻击了&#xff0c;一定要拔网线吗&#xff1f;还有…

5.30黄金空头能否延续?今日多空如何布局?

近期有哪些消息面影响黄金走势&#xff1f;今日黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周一(5月29日)进入欧市盘中&#xff0c;对美国周末达成债务上限协议的乐观情绪推动风险情绪回升&#xff0c;与此同时&#xff0c;上周公布的美国PCE数据让美联储难…

可持续能源技术改变世界

文章目录 一、你在工作或生活中接触过可持续能源技术吗&#xff1f;可以分享下你的经历与看法。二、你认为可持续能源技术的优势和挑战有哪些&#xff1f;三、你了解过可持续能源技术的应用现状吗&#xff1f;四、对于可持续能源技术真的否改变世界这个问题你怎么看&#xff1f…

Leetcode 2455 可被三整除的偶数的平均值

Leetcode 2455 可被三整除的偶数的平均值 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/average-value-of-even-numbers-that-are-divisible-by-three/description/ 博主Github&#xff1a;https://github.com/GDUT-…

创新的内容创作与电子商务完美结合!下载带有ChatGPT AI的Porto WooCommerce主题!

如果你是一位电子商务网站的所有者&#xff0c;同时追求创新和卓越的内容创作&#xff0c;那么你一定不能错过Porto主题&#xff01;Porto是一款功能强大的WooCommerce主题&#xff0c;它不仅提供了卓越的电商功能&#xff0c;还集成了ChatGPT AI内容创作&#xff0c;为你的网站…

idea使用Alibaba Cloud Toolkit插件远程操作Docker

idea使用Alibaba Cloud Toolkit插件远程操作Docker 前言 从github下载的开源项目源码&#xff0c;你基本上都能在项目根目录下发现会有个Dockerfile文件&#xff0c;Dockerfile文件是记录构建docker容器的构建命令&#xff0c;用途&#xff1a;一般用来将本地的jar包远程传输到…