JVM拾遗(5): 对象大小计算
JVM拾遗-4-Java对象的内存布局讲了JVM如何实例化对象以及对象在内存中的表示. 本次讲解如何计算一个对象的大小.
为啥要讲这个? 因为笔者在做一块业务的时候,老板有次让从数据库取100000条天气数据,在内存里按业务规则排序. 这明显不合理会让内存爆炸的需求,怎么怼回去呢?
这时候就要搬出我们的理论支持来计算一番了.
1. 数据类型及大小
这个基本功不扎实的同学可能不清楚, 不过相信大部分老司机都知道.
JVM的数据类型分为基本数据类型
和引用数据类型
.
基本数据类型有:
long
/double
: 8字节, 长整型和双精度浮点型int
/float
: 4字节, 整数和浮点数char
,short
: 2字节,字符型和短整型byte
: 1字节, 整数
在JDK8, 64位HotSpot
上, 引用数据类型都是直接指针, 如果开了压缩指针,就是4字节,没开就是8字节
用原生数据类型就是为了提高性能的.
后来为了满足一切皆对象的概念和泛型系统,出了一堆包装类, 造成装箱和拆箱的一堆性能问题不说, 还浪费内存.
比如一个int
原生类型才4字节,而Integer
包装类对象头就至少12字节了..
2. 对象的大小包含哪些
前面讲过一个对象包含3部分数据:
- 对象头(Object Header)
- 实例数据(Instance Data)
- 对齐填充(Padding)
对象头前面说过, 在64位的虚拟机上开了压缩指针就是12字节,没开就是16字节. 实例数据的大小依据数据类型的大小来计算, 注意要子类的对象大小要把父类的实例数据大小也计算进去.
对齐填充是按照对象里最宽的数据类型的大小来对齐的, 比如最大的是long
8字节, 那么就是按照8的倍数来对齐.
3. 案例
把前面提到的天气数据类简化,算算100000个天气对象的大小,如下:
|
|
按照理论,开启压缩指针后,对象头占12字节, 实例数据最长的pm25是8个字节, int是4字节, String是引用类型,占4字节, 按照8字节对齐. 总共是12+8+4+4=28字节,按照8字节对齐是32字节,要4个字节的对齐填充.
Java Object Layout, JOL
JOL是openjdk提供的用来验证JVM的内存布局方案的工具.
工具内部使用了Unsafe
, JVMTI
(工具接口, 后面会聊), ServiceabilityAgent
(SA)来计算,比基于堆转储(heap dump)出来的会更精确.
我们可以去maven仓库挑个版本将jar包下下来使用.
|
|
然后这样使用:
|
|
得到的结果(JVM默认开启压缩指针):
WeatherDO object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) a3 11 01 f8 (10100011 00010001 00000001 11111000) (-134147677)
12 4 int WeatherDO.uvLevel 0
16 8 double WeatherDO.pm25 0.0
24 4 java.lang.String WeatherDO.tips null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
可以看到,Instance size=32字节, 和我们的计算一致, 十万条对象数据就是3M. 针对笔者当时的业务场景,这是一个频繁的需求,一小块业务就几M的对象, 高并发下就杯具了:)
4. 总结
本节回顾了java对象的内存布局,并指出Hotspot
虚拟机实现下对象的大小计算方法.
- 原文作者:Chris Wang
- 原文链接:https://www.sound2gd.wang/post/jvm%E6%8B%BE%E9%81%975-%E5%AF%B9%E8%B1%A1%E5%A4%A7%E5%B0%8F%E8%AE%A1%E7%AE%97/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。