计算一个对象在堆中的大小

 2023-01-04
原文作者:lxxlxxl 原文地址:https://juejin.cn/post/7006965838867595295

计算一个对象在堆中的大小

对象的内存布局

202301011546236311.png

202301011546242542.png

示例

三种计算对象大小的方法链接:blog.csdn.net/yunqiinsigh…

使用Unsafe类来获取对象的大小。

点评:使用Unsafe可以完全不care对象内的复杂构成,可以很精确的计算出对象头的大小(即第一个字段的偏移)及每个字段的偏移。缺点是Unsafe通常禁止开发者直接使用,需要通过反射获取其实例,另外,最后一个字段的大小需要手工计算。其次需要手工写代码递归计算才能得到对象及其所引用的对象的综合大小,相对比较麻烦。

示例代码如下

    public class CalculateObjectSize {
        private static class User{
            private int id;
            private String name;
            private int age;
            private User friend;
    ​
            public User(){}
    ​
            public User(int id,String name,int age){
                this.id=id;
                this.name=name;
                this.age=age;
                this.friend=new User();
            }
        }
    ​
        public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Unsafe unsafe = (Unsafe) field.get(null);
    ​
            User user=new User(1,"xhl",23);
            System.out.println("id起始偏移量:"+unsafe.objectFieldOffset(user.getClass().getDeclaredField("id")));
            System.out.println("name起始偏移量:"+unsafe.objectFieldOffset(user.getClass().getDeclaredField("name")));
            System.out.println("age起始偏移量:"+unsafe.objectFieldOffset(user.getClass().getDeclaredField("age")));
            System.out.println("friend起始偏移量:"+unsafe.objectFieldOffset(user.getClass().getDeclaredField("friend")));
    ​
            while (true);//这里用jps jinfo来查看参数是否设置成功
        }
    }

问题1:对象头大小

答案

问1: 64位虚拟机中,对象头的大小是多少?

答:若开启类指针压缩则类指针大小位4Bytes,此时对象头大小为8+4=12Bytes,否则为8+8=16Bytes。

验证

验证如下(Jdk8)

-XX:+UseCompressedClassPointers开启(默认也是开启的),忽略图中的Jdk-15,实际运行是Jdk-8

202301011546248853.png

此时的内存布局大概是这样:

202301011546253574.png

-XX:-UseCompressedClassPointers(关闭),id的起始偏移量变成了16。

202301011546257795.png

此时的内存布局大概是这样:

202301011546262336.png

问题2:对象实例数据大小

答案

问2:这个对象的实例数据大小是多少?

答:若开启普通对象指针压缩则每个引用类型指针大小为4Bytes,此时实例数据大小为4+4+4+4=16Bytes,否则:4+4+8+8=24Bytes。

验证

验证如下(Jdk8)

开启普通对象指针压缩:-XX:+UseCompressedOops(默认也是开启的)oops: ordinary object pointer

202301011546270797.png

通过计算(28-24)可得出name引用变量的大小为4Bytes。

关闭普通对象指针压缩:-XX:-UseCompressedOops

202301011546275378.png 通过计算(32-24)可得出name引用变量的大小为8Bytes。

类指针压缩和普通对象指针压缩都开启的时候对象内存布局

如下图:对象的大小为4*8=32Bytes

202301011546280969.png

两个都关闭并且假设User类没有age这个字段,对象的内存布局

如下图:对象的大小为5*8=40Bytes。

2023010115462878310.png

也可以开一个关一个试试。

最后

1、CompactFields在Jdk14中被弃用了。

    -XX:+/-CompactFields was deprecated in version 14.0 and will likely be removed in a future release.

2、实际上一个对象占用的内存不止这么大,因为它有的字段是引用类型,引用类型还引用其他的对象,这样就更大了。但从字面上理解,这些不算这个对象的内容。

3、学习JVM中的一些记录,以后复习可能会用到。有啥子问题,请评论联系~ 参考: