如何评估java对象大小

原文链接: https://ww.cnblogs.com/Kidezyq/p/8030098.html

When—什么时候需要知道对象的内存大小

在内存足够用的情况下我们是不需要考虑java中一个对象所占内存大小的。但当一个系统的内存有限,或者某块程序代码允许使用的内存大小有限制,又或者设计一个缓存机制,当存储对象内存超过固定值之后写入磁盘做持久化等等,总之我们希望像写C一样,java也能有方法实现获取对象占用内存的大小。

How—java怎样获取对象所占内存大小

在回答这个问题之前,我们需要先了解java的基础数据类型所占内存大小。

类型 字节
byte 1
short 2
int 4
long 8
float 4
double 8
char 2
boolean 1
refrerence 4/8

当然,java作为一种面向对象的语言,更多的情况需要考虑对象的内存布局,java对于对象所占内存大小需要分两种情况考虑:

对象类型 内存布局构成
一般非数组对象 8个字节对象头(mark) + 4/8字节对象指针 + 数据区 + padding内存对齐(按照8的倍数对齐)
数组对象 8个字节对象头(mark) + 4/8字节对象指针 + 4字节数组长度 + 数据区 + padding内存对齐(按照8的倍数对齐)

可以看到数组类型对象和普通对象的区别仅在于4字节数组长度的存储区间。而对象指针究竟是4字节还是8字节要看是否开启指针压缩。Oracle JDK从6 update 23开始在64位系统上会默认开启压缩指针http://rednaxelafx.iteye.com/blog/1010079。如果要强行关闭指针压缩使用-XX:-UseCompressedOops。

接下来我们来举例来看实现java获取对象所占内存大小的方法:
假设我们有一个类的定义如下:

 1     private static class ObjectA {  
 2         String str;   // 4  
 3         int i1;       // 4  
 4         byte b1;      // 1  
 5         byte b2;      // 1  
 6         int i2;       // 4   
 7         ObjectB obj;  //4  
 8         byte b3;      // 1  
 9     }  
10 
11     private static class ObjectB {  
12         
13     }

如果我们直接按照上面掌握的java对象内存布局进行计算,则有:

Size(ObjectA) = Size(对象头(_mark)) + size(oop指针) + size(数据区)
Size(ObjectA) = 8 + 4 + 4(String) + 4(int) + 1(byte) + 1(byte) + 2(padding) + 4(int) + 4(ObjectB指针) + 1(byte) + 7(padding)
Size(ObjectA) = 40

我们直接通过Unsafe获取java对象内存占用大小来验证我们的计算是否正确。

使用Unsafe来获取对象实际大小

关于Unsafe的使用,后面我会专门开一个专题来详细讲述,这里暂时让我们来见识下Unsafe的神奇之处。

 1     private final static Unsafe UNSAFE;
 2     // 只能通过反射获取Unsafe对象的实例
 3     static {
 4         try {
 5             UNSAFE = (Unsafe) Unsafe.class.getDeclaredField("theUnsafe").get(null);
 6         } catch (Exception e) {
 7             throw new Error();
 8         }
 9     }
10 
11     Field[] fields = ObjectA.class.getDeclaredFields();
12     for (Field field : fields) {
13       System.out.println(field.getName() + "---offSet:" +UNSAFE.objectFieldOffset(field));
14     }

输出结果为:

str---offSet:24
i1---offSet:12
b1---offSet:20
b2---offSet:21
i2---offSet:16
obj---offSet:28
b3---offSet:22

我们同样可以算得对象实际占用的内存大小:

Size(ObjectA) = Size(对象头(_mark)) + size(oop指针) + size(排序后数据区) = 8 + 4 + (28+4-12) = 32.

我们再回过头来,看我们在通过代码获取对象所占内存大小之前的预估值40。比我们实际算出来的值多了8个字节。通过Unsafe打印的详细信息,我们不难想到这其实是由hotspot创建对象时的排序决定的:
HotSpot创建的对象的字段会先按照给定顺序排列,默认的顺序为:从长到短排列,引用排最后: long/double –> int/float –> short/char –> byte/boolean –> Reference。

所以我们重新计算对象所占内存大小得:
Size(ObjectA) = Size(对象头(_mark)) + size(oop指针) + size(排序后数据区)
Size(ObjectA) = 8 + 4 + 4(int) + 4(int) + byte(1) + byte(1) + 2(padding) + 4(String) + 4(ObjectB指针)
Size(ObjectA) = 32

与上面计算结果一致。

你可能感兴趣的:(java,Spring)