欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

计算 java对象 内存占用大小

程序员文章站 2022-07-12 16:50:36
...

前言

作为小白,关于Java对象的大小还停留在 8种基本类型和其包装类占用几个字节的
阶段。最近,突然发现Java对象的内存大小似乎不这么简单,就想暂且写点东西,记录一下新知识。

Java对象内存结构

总体上,提到java对象内存大小,主要分为 数组类型和非数组类型 两大类来探究。
就如同下图一样,所有的java对象一般包括 对象头(Header),实例数据(Instance data),对齐填充(padding)三部分,其中padding根据需要而存在。
计算 java对象 内存占用大小
不同的操作系统,其对象头的各部分大小如下:
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 jvm