我们天天都在使用java来new对象,但估计很少有人知道new出来的对象到底长的什么样子?对于普通的java程序员来说,可能从来没有考虑过java中对象的问题,不懂这些也可以写好代码。今天,给大家介绍一款工具JOL,可以满足大家对java对象的所有想象。

1、JOL介绍

JOL的全称是Java Object Layout 即 java 对象内存布局。是一个用来分析JVM中Object布局的小工具。包括Object在内存中的占用情况,实例对象的引用情况等等。

JOL可以在代码中使用,也可以独立的以命令行中运行。命令行的我这里就不具体介绍了,今天主要讲解怎么在代码中使用JOL。

使用JOL需要添加maven依赖:

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

2、java对象结构说明

对象的结构包括:

对象头、

对象体、

对齐字节(可有可无,若对象头加上对象体是8的倍数时,则不存在字节对齐)。

2.1、对象头

对象头包含三部分,Mark Word、class point、数组长度。如果对象不是数组,数组长度可以忽略。

Hotspot 64位实现

2.1.1、Mark Word

markword 固定长度8byte,描述对象的identityhashcode,分代年龄,锁信息等。

以下是 Java对象处于5种不同状态时,Mark Word中 64位的表现形式,上面每一行代表对象处于某种状态时的样子。其中各部分的含义如下:

 * MarkWord的构成如下:* ------------------------------------------------------------------------------|-----------|*                     Mark Word(64 bits)                                        |  锁状态   |* ------------------------------------------------------------------------------|-----------|* unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:0 | lock:01 |  正  常   |* ------------------------------------------------------------------------------|-----------|* thread:54 |    epoch:2    |        unused:1 | age:4 | biased_lock:1 | lock:01 |  偏向锁   |* ------------------------------------------------------------------------------|-----------|*                     ptr_to_lock_record:62                           | lock:00 |  轻量级锁 |* ------------------------------------------------------------------------------|-----------|*                 ptr_to_heavyweight_monitor:62                       | lock:11 |  重量级锁 |* ------------------------------------------------------------------------------|-----------|*                                                                     | lock:11 |  GC标记   |* ------------------------------------------------------------------------------|-----------|

说明:

1、lock。2位,锁状态的标记位
2、biased_lock。1位。对象是否存在偏向锁标记lock与biased_lock共同表示锁对象处于什么锁状态。
3、age。4位,表示JAVA对象的年龄,在GC中,当survivor区中对象复制一次,年龄加1,如果到15之后会移动到老年代,并发GC的年龄阈值为6.
4、identity_hashcode。31位,调用方法 System.identityHashCode()计算,并会将结果写到该对象头中。当对象加锁后(偏向、轻量级、重量级),MarkWord的字节没有足够的空间保存hashCode,因此该值会移动到线程 Monitor中。
5、thread。54位,持有偏向锁的线程ID(此处的线程id是操作系统层面的线程唯一Id,与java中的线程id是不一致的,了解即可)。
6、epoch。2位,偏向锁的时间戳。
7、ptr_to_lock_record。62位,轻量级锁状态下,指向栈中锁记录的指针。
8、ptr_to_heavyweight_monitor。62位,重量级锁状态下,指向对象监视器 Monitor的指针。

mark word中锁状态描述(根据后三位判断)

偏向锁位 1bit(是否偏向锁) 锁标志位 2bit 锁状态
0(代表无锁) 01 无锁态(new)
1(偏向锁) 01 偏向锁
- 00 轻量级锁(自旋锁、无锁、自适应自旋锁)
- 10 重量级锁
- 11 GC 标记

2.1.2、Class point(类指针)

这一部分用于存储对象的类型指针,该指针指向它的类元数据,JVM通过这个指针确定对象是哪个类的实例。该指针的位长度为JVM的一个字大小,即 32位的JVM 为 32位,64位的JVM为 64位。

如果应用的对象过多,使用 64位的指针将浪费大量内存,统计而言,64位的 JVM将会比 32位的 JVM多耗费 50%的内存。为了节约内存可以使用选项 +UseCompressedOops开启指针压缩,其中,oop即 ordinary object pointer普通对象指针。开启该选项后,下列指针将压缩至32位:

【1】每个 Class的属性指针(即静态变量);
【2】每个对象的属性指针(即对象变量);
【3】普通对象数组的每个元素指针;
当然,也不是所有的指针都会压缩,一些特殊类型的指针 JVM不会优化,比如指向 PermGen的 Class对象指针(JDK8中指向元空间的 Class对象指针)、本地变量、堆栈元素、入参、返回值和NULL指针等。

在64位jvm虚拟机中mark word、Class pointer这两部分都是64位的,所以也就是需要128位大小(16 bytes)。

    注意:64位虚拟机中在堆内存小于32GB的情况下,UseCompressedOops是默认开启的,该参数表示开启指针压缩,会将原来64位的指针压缩为32位。

  • 开启(-XX:+UseCompressedOops) 可以压缩指针。
  • 关闭(-XX:-UseCompressedOops) 可以关闭压缩指针。

2.1.3、数组长度

如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度,这部分数据的长度也随着 JVM架构的不同而不同:32位的JVM上,长度为32位;64位JVM则为64位。64位 JVM如果开启 +UseCompressedOops选项,该区域长度也将由64位压缩至32位。

2.2、对象体

对象体中表示对象中的内容,如对象中每个属性。

2.3、对齐字节

为了方便虚拟机的寻址,比如64位的虚拟机中对象不能被64整除,会补齐对应位。

java的基础数据类型所占内存情况如下表格:

boolean byte short char int long float double
1 bytes 1 bytes 2 bytes 2 bytes 4 bytes 8 bytes 4 bytes 8 bytes

3、基础概念:

问题1. Java对象如何存储?

对象的实例(instantOopDesc)保存在堆上,

对象的元数据(instantKlass,即class文件)保存在方法区(元空间),

对象的引用保存在栈上。

问题2:指针压缩

开启指针压缩可以减少对象的内存使用。

在关闭指针压缩时,String、Integer等字段由于是引用类型,因此分别占8个字节;

而开启指针压缩之后,这两个字段只分别占用4个字节。

因此,开启指针压缩,理论上来讲,大约能节省接近百分之五十的内存。(如果对象属性都是引用类型的话)

jdk8及以后版本已经默认开启指针压缩,无需配置。

4、示例

4.1、空属性-对象布局

public static void main(String[] args) {Object object = new Object();System.out.println(ClassLayout.parseInstance(object).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/* 说明:前两个头为mark word,总共占用 8个字节;第三个头是 class point , 不采用指针压缩,占用 8个字节;采用指针压缩后,占用 4个字节。*/
  • OFFSET:偏移地址,单位字节;
  • SIZE:占用的内存大小,单位为字节;
  • TYPE DESCRIPTION: 类型描述,其中object header为对象头;
  1. object header:对象头;
  2. loss due to the next object alignment:由于对象对齐而导致的丢失(有4Byte是对齐的字节(因为在64位虚拟机上对象的大小必须是8的倍数),由于这个对象里面没有任何字段,故而对象的实例数据为0Byte)。
  • VALUE : 对应内存中当前存储的值;
  • Instance size:实例字节数值大小(**此处一个空的java对象(不包含任意字段属性)实例,其实例大小为``16Byte**)。

 4.2、有属性-对象布局

public static void main(String[] args) {Student o = new Student();System.out.println(ClassLayout.parseInstance(o).toPrintable());
}// 输出结果(默认开启指针压缩):
com.brown.Student 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     java.lang.Integer Student.age               016    4     java.lang.String Student.name              null20    4     (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total// 输出结果(关闭指针压缩)【(-XX:-UseCompressedOops)】:com.brown.Student 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)           30 35 64 1c (00110000 00110101 01100100 00011100) (476329264)12    4              (object header)           00 00 00 00 (00000000 00000000 00000000 00000000) (0)16    8    java.lang.String Student.name                      null24    8    java.lang.Integer Student.age                      null
Instance size: 32 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

指针压缩对于内存的优化:

开通指针压缩时,该对象所占的内存是:24Byte,

 关闭指针压缩时,该对象所占的内存是:32Byte,节省25%的内存

​ 对象头大小的变化:

​        关闭指针压缩时,对象头中元数据指针为Klass类型,占用8个字节;

开启指针压缩时,对象头中元数据指针为narrowKlass 类型,占用8个字节。

4.3、关于锁-对象布局

此处以synchronized为例,分析MarkWord中对象锁信息的存储情况。

public static void main(String[] args) {User user = new User();synchronized (user) {// 上锁后,打印对象内存布局System.out.println(ClassLayout.parseInstance(user).toPrintable());}
}// 输出结果
com.example.entity.User object internals:OFFSET  SIZE                             TYPE DESCRIPTION                               VALUE0     4                                  (object header)                           e8 f5 b0 02 (11101000 11110101 10110000 00000010) (45151720)4     4                                  (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4                                  (object header)                           94 ef 00 f8 (10010100 11101111 00000000 11111000) (-134156396)12     4                   java.lang.Long User.id                                   null16     4                 java.lang.String User.userName                             null20     4                 java.lang.String User.passWord                             null24     4   com.example.entity.UserSexEnum User.userSex                              null28     4                 java.lang.String User.nickName                             null
Instance size: 32 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

可以看出,现在打印的第一行Mark Word 结果,已经和 上面 4.2 中的输出结果不一样了。

MarkWordk为 0x0000 0000 02b0 f5e8,  二进制为 00000000 00000000 00000000 00000000  00000010 10110000 11110101 11101000

0x在Java里面是16进制的表示,0x引导的数都是十六进制数

倒数第三位为"0",说明不是偏向锁状态,倒数两位为"10",因此,是轻量级锁状态,那么前面62位就是指向栈中锁记录的指针。

另外,可以看出,执行Synchronized代码块的时候,锁定对象。

计算机用的和输出到的正好相反【把输出结果中的十六进制数倒过来拼一起就是这一串了】。这里涉及到一个知识点“大端存储与小端存储”(汇编语言)。

Little-Endian:低位字节存放在内存的低地址端,高位字节存放在内存的高地址端。
Big-Endian:高位字节存放在内存的低地址端,低位字节存放在内存的高地址端。

5、使用JOL分析VM信息

常用的方法:

计算对象的大小(单位为字节):ClassLayout.parseInstance(obj).instanceSize()
查看对象内部信息: ClassLayout.parseInstance(obj).toPrintable()
查看对象外部信息:包括引用的对象:GraphLayout.parseInstance(obj).toPrintable()
查看对象占用空间总大小:GraphLayout.parseInstance(obj).totalSize()

JOL(java object layout): java 对象内存布局相关推荐

  1. JOL(java object layout --java 对象内存布局)

    JOL(java object layout --java 对象内存布局) ⚠⚠⚠本文以java普通对象为切入点,分析java的对象内存布局,数组见文末 maven地址

  2. Java对象内存布局(JOL)

    前言 Java对象的内存布局主要由对象头(Object Header).实例数据(instance data).对齐填充(padding)三部分组成. 对象头:存储对象的基础信息(如锁状态.GC状态. ...

  3. openjdk jol 工具打印 Java 对象内存布局

    1.maven 依赖 <dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-cor ...

  4. 深入浅出Java Object Layout (JOL)

    基本介绍和使用 Java Object Layout (JOL) openjdk/jol github maven <!-- https://mvnrepository.com/artifact ...

  5. java对象内存布局及大小

    查看虚拟机配置(以hotspot 64bit 虚拟机为例) java -XX:+PrintCommandLineFlags -version 输出结果如下图,表示当前虚拟机的配置信息.每个人的机器可能 ...

  6. java char占用多少字节_Java虚拟机:Java对象大小、对象内存布局及锁状态变化

    一个对象占多少字节? 关于对象的大小,对于C/C++来说,都是有sizeof函数可以直接获取的,但是Java似乎没有这样的方法.不过还好,在JDK1.5之后引入了Instrumentation类,这个 ...

  7. java对象内存布局中的基本类型字段排列顺序

    java对象内存布局: mark word(记录hashCode值和锁的标识等等) class对象指针 类字段 补齐位 如果是数组对象,2.3之间应该加上  数组长度 布局排列表: 32位jdk 普通 ...

  8. 22-10-14 西安 spring循环依赖、对象内存布局、synchronized锁升级

    关于锁升级参考了周阳老师在b站的juc视频,阳哥讲的很好 尚硅谷2022版JUC并发编程(对标阿里P6-P7)_哔哩哔哩_bilibili spring循环依赖 1.循环依赖问题 什么是循环依赖 默认 ...

  9. 【C++】C++对象模型:对象内存布局详解(C#实例)

    C++对象模型:对象内存布局详解 0.前言 C++对象的内存布局.虚表指针.虚基类指针解的探讨,参考. 1.何为C++对象模型? 引用<深度探索C++对象模型>这本书中的话: 有两个概念可 ...

最新文章

  1. java 自定义注解
  2. 中原银行签约神策数据 数据驱动构建智能服务体系
  3. Spring Boot通过url设置国际化
  4. 蓝桥杯入门训练序列求和
  5. VM虚拟机Linux克隆后网卡的相关操作
  6. Linux之|etc|group文件
  7. Golang使用心得
  8. 01-vue项目之滚动加载数据
  9. 早上收到这样一份通知,求一无漏洞框架,无力吐槽
  10. vmware ubuntu 16.04 guest 修复不能桌面大小自动调整和从宿主机复制粘贴的问题
  11. selenium实例:自动刷青马网课实现登陆 python实现自动登陆 利用pytesseract自动识别验证码并登录
  12. Multisim10.0.1汉化时没有stringfiles问题
  13. 菲尼克斯电源模块QUINT-PS3AC24DC40的组装说明
  14. System Repair Engineer (SREng) 2.6 正式发布
  15. yolov 论文发表在什么期刊上_joe期刊是什么期刊_joe是什么意思_joe牌子
  16. STAF 删除文件操作
  17. RS笔记:传统推荐模型之FFM (引入特征域的概念) [2015 Criteo]
  18. 固网服务器win7系统驱动,固网HU-1608n驱动
  19. Symmetric and anti-symmetric BCs in FDTD and MODE
  20. I2C的ACK和NACK

热门文章

  1. K860i的109升级需要的PinyinIME.apk和QuickSearchBox.apk两个文件
  2. Breeze's MapHack 1.0 正式版发布【修正版发布】
  3. 全球及中国视频会议摄像机行业市场运营模式与投资战略规划研究报告2022-2028年
  4. Java 超.简易RPG游戏
  5. TCP/IP五层协议体系结构的各层功能
  6. 静态链接之与静态库的链接
  7. Matlab版本对应CUDA
  8. SSD Performance测试简介
  9. 2021年质量员-装饰方向-岗位技能(质量员)最新解析及质量员-装饰方向-岗位技能(质量员)试题及解析
  10. SIP注册信令消息示范及解释