文章目录
- 前言
- 一、Java对象的内存布局
- 二、JOL引入
- 三、对象分析
-
- 1.Object对象
- 2.数组对象
- 3.自定义对象
- 四、指针压缩
- 总结
前言
要想查看java对象在虚拟机的内存占用情况,我们可以使用OpenJDK官方提供的JOL(Java Object Layout)工具,即可很方便分析、了解一个Java对象在内存当中的具体布局情况。这里将在64位的HotSpot Java虚拟机环境下进行分析、测试
一、Java对象的内存布局
Java的实例对象、数组对象在内存中的组成包括如下三部分:对象头Hearder、实例数据、内存填充。示意图如下所示
- 对象头:对象头主要包括两部分数据:Mark Word、Class对象指针,对于数组对象而言,其还包括了数组长度数据。在64位的HotSpot虚拟机下,Mark Word占8个字节,其记录了Hash Code、GC信息、锁信息等相关信息;而Class对象指针则指向该实例的Class对象,在开启指针压缩的情况下占用4个字节,否则占8个字节;如果其是一个数组对象,则还需要4个字节用于记录数组长度信息。
- 实例数据:
- 内存对齐:即内存填充,所有对象内存大小必须被8整除
二、JOL引入
引入最新版本的jol的maven依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
三、对象分析
1.Object对象
代码如下(示例):
/**
* java.lang.Object object internals:
* OFF SZ TYPE DESCRIPTION VALUE
* 0 8 (object header: mark) 0x0000004f8e5cde01 (hash: 0x4f8e5cde; age: 0)
* 8 4 (object header: class) 0xf80001e5
* 12 4 (object alignment gap) 对齐
* Instance size: 16 bytes
* Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
*/
Object t = new Object();
System.out.println(ClassLayout.parseInstance(t).toPrintable());
2.数组对象
/**
* OFF SZ TYPE DESCRIPTION VALUE
* 0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
* 8 4 (object header: class) 0xf80001a9
* 12 4 (array length) 2 数组长度
* 12 4 (alignment/padding gap) 对齐
* 16 16 long [J.<elements> N/A 数组节点为long型,每个长度为8
* Instance size: 32 bytes
* Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
*/
long [] arryas = new long[2];
System.out.println(ClassLayout.parseInstance(arryas).toPrintable());
3.自定义对象
用户实体信息
public class User {
private String name;
private int age;
private Integer sex;
private int [] arryas = new int[5];
private long height;
}
对象内存信息
/**
* OFF SZ TYPE DESCRIPTION VALUE
* 0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
* 8 4 (object header: class) 0xf80131f6
* 12 4 int User.age 0 int类型4byte
* 16 8 long User.height 0 long类型8byte
* 24 4 java.lang.String User.name null 引用类型4byte
* 28 4 java.lang.Integer User.sex null 引用类型4byte
* 32 4 int[] User.arryas [0, 0, 0, 0, 0] 引用类型4byte
* 36 4 (object alignment gap) 对齐
* Instance size: 40 bytes
* Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
*/
User t = new User();
System.out.println(ClassLayout.parseInstance(t).toPrintable());
由此可以得知各数据类型在分配内存大小的不同:
reference引用对象在32bit虚拟机占用4bytes,在64bit虚拟机上每个占用8bytes,如果开启指针压缩则每个占用4bytes。
四、指针压缩
在64位的HotSpot虚拟机下,类型指针、引用类型需要占8个字节。显然这大大增加了内存的消耗和占用。为此从JDK 1.6开始,64位的JVM支持UseCompressedOops选项。其可对OOP(Ordinary Object Pointer,普通对象指针)进行压缩,使其只占用4个字节,以达到节约内存的目的。
如果JVM的版本在 Java SE 6 update 23 及以上, 则不需要再设置 -XX:+UseCompressedOops 参数, 因为默认会开启。
-XX:+UseCompressedOops // 开启指针压缩
-XX:-UseCompressedOops // 关闭指针压缩
总结
实际工作中真正需要手动计算对象大小的场景应该很少,但是个人觉得做为基础知识每个Java开发人员都应该了解,另外:对自己写的代码大概占用多少内存,内存中是怎么布局的应该有一个直觉性的认识。