计算 java对象 内存占用大小
前言
作为小白,关于Java对象的大小还停留在 8种基本类型和其包装类占用几个字节的
阶段。最近,突然发现Java对象的内存大小似乎不这么简单,就想暂且写点东西,记录一下新知识。
Java对象内存结构
总体上,提到java对象内存大小,主要分为 数组类型和非数组类型 两大类来探究。
就如同下图一样,所有的java对象一般包括 对象头(Header),实例数据(Instance data),对齐填充(padding)三部分,其中padding根据需要而存在。
不同的操作系统,其对象头的各部分大小如下:
32位操作系统
区域 | 大小(B) |
---|---|
klass | 4 |
mark | 4 |
数组长度 | 4 |
64位操作系统(指针无压缩)
区域 | 大小(B) |
---|---|
klass | 8 |
mark | 8 |
数组长度 | 4 |
64位操作系统(指针有压缩)
区域 | 大小(B) |
---|---|
klass | 4 |
mark | 8 |
数组长度 | 4 |
Java对象大小的计算公式
根据上一小节,我们可知:
Java对象大小=对象头(klass+mark+ 数组长度)+实例数据+对齐填充区
再结合一个原则,即对象属性按照 “long/double(8B) –> int/float(4B) –> short/char(2B) –> byte/boolean(1B) –> reference” 排序,我们可得出万能公式
Java对象大小=klass+mark+array_length+实例数据(字段排序且加补齐填充)
64位操作系统实例:
public class UserVo {
private String name;//4
private int id;//4
private double money;//8
private byte health;//1
private boolean isGirl;//1
private short age;//2
}
@Test
public void testaa() {
UserVo userVo = new UserVo();
long size = RamUsageEstimator.sizeOf(userVo);
//32 = 8 +4+4(padding) +8 +4+2+1+1
//name为null,故没有值
System.out.println(size);
}
计算Java对象大小的工具
以下实例均以UserVo作为对比
public class UserVo {
private String name;
private int id;
private double money;
private byte health;
private boolean isGirl;
private short age;
}
1.sun.misc.unsafe
该法主要是反射获取unsafe实例,进而计算各字段的offset,找到最大的offset加上这个字段的占用大小,最后可能需要再加上padding,就得到了该对象的size。
private static Unsafe UNSAFE;
static {
try {
Field field = Unsafe.class.getField("theUnsafe");
field.setAccessible(true);
UNSAFE = (Unsafe) field.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void test() {
UserVo userVo = new UserVo();
long start = System.currentTimeMillis();
Field[] fields = UserVo.class.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName() + " offSet:" + UNSAFE.objectFieldOffset(field));
}
long end = System.currentTimeMillis() - start;
System.out.println("耗时: " + end + " ms");
}
name offSet:28
id offSet:12
money offSet:16
health offSet:26
isGirl offSet:27
age offSet:24
耗时: 1 ms
结果如上,找到具有最大offset的字段(name),计算为 28+4(padding)正好等于32.
2.ObjectSizeCalculator
该法需要jvm是HotSpot VM,效率很高
@Test
public void test() {
UserVo userVo = new UserVo();
long start = System.currentTimeMillis();
long size = ObjectSizeCalculator.getObjectSize(userVo);
long end = System.currentTimeMillis() - start;
//耗时: 14 ms
System.out.println("耗时: " + end + " ms");
//32
System.out.println(size);
}
3.com.carrotsearch.sizeof.RamUsageEstimator
改法主要是需要初始化静态块,即 不同操作系统中,各种对象头,数组长度等所占内存大小,耗时较久,待第一次初始化后,计算时长就会短很多。
compile group: 'com.carrotsearch', name: 'java-sizeof', version: '0.0.3'
@Test
public void test() {
UserVo userVo = new UserVo();
long start = System.currentTimeMillis();
long size = RamUsageEstimator.sizeOf(userVo);
long end = System.currentTimeMillis() - start;
//耗时: 59 ms
System.out.println("耗时: " + end + " ms");
//32
System.out.println(size);
}
上一篇: 通用套接字选项
下一篇: Java 对象大小的计算方式