1.对象的内存布局
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和 对齐填充(Padding)。下图是普通对象实例对象结构 与 数组对象实例的数据结构:
(1)Java对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。也就是 JAVA对象 = 对象头 + 实例数据 + 对象填充
(2)对象头(Header)包含三部分信息:markword、klass对象指针、length数组长度。
(1)对象头(Header)
HotSpot虚拟机的对象头包括三部分信息:markword + _klass对象指针 + 数组长度;需要注意的是32位vm 和 64位vm对应每个部分占用的大小是不一样的。
-
1)markword
用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,官方称它为“MarkWord”。 -
2)_klass
class对象指针。即对象指向它的类元数据(metadata)的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果未启用指针压缩这部分数据的长度在32位和64位的虚拟机中分别为32bit和64bit,64位vm开启compressed后_klass数据长度是32bit。
#1.JVM支持klass指针压缩,虚拟机是64位系统时默认开始了指针(compressed)压缩。
(1)jvm配置参数:UseCompressedOops来开启或禁止指针压缩(compressed--压缩、oop--对象指针)。
(2)启用指针压缩:-XX:+UseCompressedOops,禁止指针压缩:-XX:-UseCompressedOops
#2.在64位虚拟机默认开启压缩指针的环境下_klass占用32bits空间,没开启指针压缩时_klass占用64bits。
- 3)数组长度(只有数组对象才有)
如果对象是一个数组, 那在对象头中还必须有一块数据用于记录数组长度。这部分数据在的长度在32位和64位的虚拟机中都是32bit。
Java对象头里的Mark Word里默认存储对象的HashCode、GC分代年龄、偏向锁状态和锁标志位。32位JVM的Mark Word的默认存储结构(无锁状态)如表:
在运行期间,Mark Word里存储的数据会随着锁标志位的变化而变化。Mark Word每种锁标志对应的存储的数据如下四种:
(2)实例数据
实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。原生类型(primitive type)的内存占用如下:
数据类型 | 占用字节数 | 备注 |
---|---|---|
boolean | 1 | |
byte | 1 | |
short | 2 | |
char | 2 | |
int | 4 | |
float | 4 | |
long | 8 | |
double | 8 | |
reference | 4/8 | 引用类型在32位系统上每个占用4bytes, 在64位系统上每个占用8bytes(如果VM开启了指针压缩64位系统里占4bytes)。 |
(3)对齐填充
第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。hotspot vm 的自动内存管理系统要求对象起始地址比必须位 8 字节的整数倍,即对象的大小必须是 8 字节的整数倍。当对象头 + 实例数据大小不足8字节的倍数时,会进行自动填充到8字节的倍数大小。
#1.无论是64位vm,还是32位vm,对齐填充位都是满足8字节的倍数。
#2.new object()到底占多少个字节?
由于new object()没有实例数据和数组长度,只有对象头占内存空间。从上面Header存储信息可以看出在32位VM、64位VM中总共有3种对象头模型。
(1)在32位VM中:32bit的markword + 32bit的klass = 8字节。
(2)64位VM中,且不开启指针压缩:64bit的markword + 64bit的klass = 16字节
(3)64位JVM中,且开启指针压缩: 64bit的markword + 32bit的klass + 4字节的padding = 16字节
2.对象内存大小计算
OpenJDK提供了JOL包,可以帮我们计算出某个对象在运行时内存分布、占用内存大小,用它能找出一个对象究竟败了多少内存,是一个非常好的工具。
(1)jol的maven依赖
<!-- openjdk的jol工具查看对象内存分布 -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
(2)对象占用内存分析
接下来我们用两个例子来分析一个空对象 和 有实例数据的对象在运行时时期,占用内存大小。
1)空对象分析
//先声明一个空对象类
public class MyObject {
}
//测试方法
public static void main(String[] args) {
Layouter l;
//32位vm对象分布
l = new HotSpotLayouter(new X86_32_DataModel());
System.out.println("***** " + l);
System.out.println(ClassLayout.parseClass(MyObject.class, l).toPrintable());
System.out.println("==============================================");
//64位vm对象分布,未启动指针压缩
l = new HotSpotLayouter(new X86_64_DataModel());
System.out.println("***** " + l);
System.out.println(ClassLayout.parseClass(MyObject.class, l).toPrintable());
System.out.println("==============================================");
//64位vm对象分布,启动指针压缩
l = new HotSpotLayouter(new X86_64_COOPS_DataModel());
System.out.println("***** " + l);
System.out.println(ClassLayout.parseClass(MyObject.class, l).toPrintable());
}
//输出对象内存分布信息
//(1)32位vm对象分布,MyObject对象大小:8字节的header(4字节markword、4字节klass指针)
***** VM Layout Simulation (X32 model, 8-byte aligned, compact fields, field allocation style: 1)
lock.MyObject object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 8 (object header) N/A
Instance size: 8 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
==============================================
//(2)64位vm未启动指针压缩,MyObject对象大小: 16字节的header(8字节markword、8字节klass指针)
***** VM Layout Simulation (X64 model, 8-byte aligned, compact fields, field allocation style: 1)
lock.MyObject object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 16 (object header) N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
==============================================
//(3)64位vm启动指针压缩,MyObject对象大小: 12字节的header(8字节markword、4字节klass指针) + 4字节的对齐填充
***** VM Layout Simulation (X64 model (compressed oops), 8-byte aligned, compact fields, field allocation style: 1)
lock.MyObject object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
2)非空对象,有实例数据分析
//先声明一个有成员变量的非空对象类
public class MyObject {
int i;
long j;
MyObject object;
}
//测试方法
public static void main(String[] args) {
Layouter l;
//32位vm对象分布
l = new HotSpotLayouter(new X86_32_DataModel());
System.out.println("***** " + l);
System.out.println(ClassLayout.parseClass(MyObject.class, l).toPrintable());
System.out.println("==============================================");
//64位vm对象分布,未启动指针压缩
l = new HotSpotLayouter(new X86_64_DataModel());
System.out.println("***** " + l);
System.out.println(ClassLayout.parseClass(MyObject.class, l).toPrintable());
System.out.println("==============================================");
//64位vm对象分布,启动指针压缩
l = new HotSpotLayouter(new X86_64_COOPS_DataModel());
System.out.println("***** " + l);
System.out.println(ClassLayout.parseClass(MyObject.class, l).toPrintable());
}
//输出对象内存分布信息
//(1)32位vm对象分布,MyObject对象大小:8字节的header(4字节markword、4字节klass指针) + 4字节的int变量i + 8字节的long变量j + 4字节的引用变量object
***** VM Layout Simulation (X32 model, 8-byte aligned, compact fields, field allocation style: 1)
lock.MyObject object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 8 (object header) N/A
8 8 long MyObject.j N/A
16 4 int MyObject.i N/A
20 4 lock.MyObject MyObject.object N/A
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
==============================================
//(2)64位vm未启动指针压缩,MyObject对象大小: 16字节的header(8字节markword、8字节klass指针) + 4字节的int变量i + 8字节的long变量j + 8字节的引用变量object + 4字节的对齐填充
***** VM Layout Simulation (X64 model, 8-byte aligned, compact fields, field allocation style: 1)
lock.MyObject object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 16 (object header) N/A
16 8 long MyObject.j N/A
24 4 int MyObject.i N/A
28 4 (alignment/padding gap)
32 8 lock.MyObject MyObject.object N/A
Instance size: 40 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
==============================================
//(3)64位vm启动指针压缩,MyObject对象大小: 12字节的header(8字节markword、4字节klass指针) + 4字节的int变量i + 8字节的long变量j + 4字节的引用变量object + 4字节的对齐填充
***** VM Layout Simulation (X64 model (compressed oops), 8-byte aligned, compact fields, field allocation style: 1)
lock.MyObject object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int MyObject.i N/A
16 8 long MyObject.j N/A
24 4 lock.MyObject MyObject.object N/A
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total