一.对象的内存布局
在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
1.对象头
HotSpot虚拟机对象的对象头部分包括两类信息。第一类是用于存储对象自身的运行时数据,如哈希(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32个比特和64个比特,官方称它 为“Mark Word”。对象头的另外一部分是类型指针,即对象指向它的类型元数据的指针。此外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据。
2.实例数据
是对象真正存储的有效信息。
3.对齐填充 (64位机)
由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。
对象大小(64位机)
普通对象
1.对象头:
markword: 8个字节
ClassPointer指针:-XX:+UseCompressedClassPointers 为4字节 不开启为8字节
2.实例数据
引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节
3.Padding对齐,8的倍数
数组对象
- 对象头:markword 8
- ClassPointer指针同上
- 数组长度:4字节
- 数组数据
- 对齐 8的倍数
二.使用JavaAgent测试Object的大小
1.观察虚拟机配置
java -XX:+PrintCommandLineFlags -version
zhangtianci@zhangtinsidembp ~ % java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=536870912 -XX:MaxHeapSize=8589934592 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
java version "1.8.0_251"
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)
2.新建项目ObjectSize (1.8)
package com.zhangtianci.jvm.agent;
import java.lang.instrument.Instrumentation;
public class ObjectSizeAgent {
private static Instrumentation inst;
public static void premain(String agentArgs, Instrumentation _inst) {
inst = _inst;
}
public static long sizeOf(Object o) {
return inst.getObjectSize(o);
}
}
-
src目录下创建META-INF/MANIFEST.MF
Premain-Class: com.zhangtianci.jvm.agent.ObjectSizeAgent
-
打包jar文件
-
在需要使用该Agent Jar的项目中引入该Jar包
project structure - project settings - library 添加该jar包 -
运行时需要该Agent Jar的类,加入参数:
-javaagent:\xxx\ObjectSize.jar
-
如何使用该类:
public class SizeOfAnObjectTest { public static void main(String[] args) { System.out.println(ObjectSizeAgent.sizeOf(new Object())); System.out.println(ObjectSizeAgent.sizeOf(new int[] {})); System.out.println(ObjectSizeAgent.sizeOf(new P())); } private static class P { //8 _markword //4 _oop指针 int id; //4 String name; //4 int age; //4 byte b1; //1 byte b2; //1 Object o; //4 byte b3; //1 } }
Hotspot开启内存压缩的规则(64位机)
- 4G以下,直接砍掉高32位
- 4G - 32G,默认开启内存压缩 ClassPointers Oops
- 32G,压缩无效,使用64位
内存并不是越大越好(-)
三.对象定位
在创建对象之后,可通过栈上的reference数据操作堆上的具体对象。这个reference有两种比较主流
的实现方式。
1.句柄:这种方式Java堆需要分配一块内存作为句柄池,reference存的就是句柄地址。句柄包含了对
象实例数据地址和对象类型数据地址。
2.直接指针:这种方式reference存的直接是对象地址,如果是访问对象本身则比句柄少了一次访问开
销。
hotspot 采用直接指针的方式。