JVM深度理解(一):一个对象到底有多大
java对象内存模型
截图.png
对象头:Instance Header,存储了Java对象hash、GC年龄、锁标记、class指针、数组长度等信息。在64位系统中,其中mark占8字节,klass指针区在打开指针压缩时,占4字节,关闭指针压缩时占8字节。指针压缩开关默认打开,通过-XX:-UseCompressedOops控制关闭。
整个对象的大小,由header(对象头),body(数据),padding(数据补齐)3个部分组成,VM要求对象大小须是8的整体数,该部分是为了让整体对象在内存中的地址空间大小达到8的整数倍而额外占用的字节数。
【length】存的内容
可以通过以下例子直观的感受每个对象的大小和数据补齐,指针压缩的的效果。
当vm参数中关闭指针压缩时,8个基本类型的对象大小如下
截图2.png
当vm参数中打开指针压缩时
截图3.png通过以上例子,我们了解了一个基本类型对象的大小,和是否打开指针压缩的区别。但在我们实际编码中,使用最多的对象往往不是基本类型,而是String.
那么一个String对象又有多大呢?我们继续看以下例子(下面的截图均是打开了指针压缩的运行结果)
截图4.png从这个例子可以看出来,一个空String的大小就有40字节,而随着String里字符串内容的增加,所占用的内存也会增加,大概是每增加一个字符,增加2字节,原因其实也很好解释,因为String的主要数据存储就是一个char数据,每增加一个字符,就相当于增加了一个char字符,也就是2字节。
截图5.png接着我们来看我们实际场景中一个常用对象呢
截图6.png 截图7.png可以看出对象的大小即是自己本身大小,加上里面属性的大小(如果属性值为空也还会有一个引用大小4,从空的testModel1和testModel2的大小可以看出)。那我们现在来估算一下,
1个list里面有10个TestModel2对象,每个TestModel2对象有10个String对象,每个String对象长度为4.这样一个list会占用多少内存?
单个TestModel对象,(24+40+4)10+8+4+4(对齐补充)= 536字节
10个TestModel就会是5360加上list对象中的其他属性应该是略大于5360的。(注意如果属性之间的值有很多相等的,这个大小会大幅减少,应该会复用同一个引用地址)
截图8.png相信通过这之前的描述,我们已经对一个对象占用多大的空间有了一个大概的概念,那么在实际开发中了解这些又有什么用呢。
1.在查询整张表的数据时,如果表中数据较大,应该多次查询。
2.在使用线程池时,使用队列时,尽可能指明队列长度,一个无界的队列很容易把jvm打爆。
接下来我们来模拟下,在-Xmx256M 的环境下一个多大的list会把内存打爆。
截图9.png可以发现一个10个属性的简单对象,只需要20w不到的数据量就可以把256m的内存打爆,发生oom,而且这还是在没有其他并发的逻辑的情况下,真实的生产环境,能支撑的数据量更少。
所以,在我们平常代码中,还是需要注意全量查询和队列的场景,都是oom的高发点。
第一部分到这里也就结束了。这里遗留了一个问题,在195000个对象后,整个list的大小大概只有122m,为啥会把256的堆内存打爆?下一节我们会揭开这个问题的答案。