对象内存计算神器

今天给大家介绍一个对象内存计算神奇。jvm内存溢出的时候,我们可以通过很多方法查看原因,很多时候也需要查看具体是哪一个大对象导致内存溢出。

image

这里要介绍的是lucene提供的专门用于计算堆内存占用大小的工具类:RamUsageEstimato

maven坐标:

1
2
3
4
5
6
<!--加载内存查看工具-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.0.0</version>
</dependency>

RamUsageEstimator就是根据java对象在堆内存中的存储格式,通过计算Java对象头、实例数据、引用等的大小,相加而得,如果有引用,还能递归计算引用对象的大小。RamUsageEstimator的源码并不多,几百行,清晰可读。这里不进行一一解读了。它在初始化的时候会根据当前JVM运行环境、CPU架构、运行参数、是否开启指针压缩、JDK版本等综合计算对象头的大小,而实例数据部分则按照java基础数据类型的标准大小进行计算。思路简单,同时也在一定程度上反映出了Java对象格式的奥秘!

常用方法API:

1
2
3
4
5
6
7
8
9
//计算指定对象及其引用树上的所有对象的综合大小,单位字节
long RamUsageEstimator.sizeOf(Object obj)

//计算指定对象本身在堆空间的大小,单位字节
long RamUsageEstimator.shallowSizeOf(Object obj)

//计算指定对象及其引用树上的所有对象的综合大小,返回可读的结果,如:2KB

String RamUsageEstimator.humanSizeOf(Object obj)

点评:使用该第三方工具比较简单直接,主要依靠JVM本身环境、参数及CPU架构计算头信息,再依据数据类型的标准计算实例字段大小,计算速度很快,另外使用较方便。如果非要说这种方式有什么缺点的话,那就是这种方式计算所得的对象头大小是基于JVM声明规范的,并不是通过运行时内存地址计算而得,存在与实际大小不符的这种可能性。

具体demo案例:

1
2
3
4
5
6
7
8
9
10
List<OrderDTO> list = new ArrayList<>();
for(int i=0;i<10;i++){
OrderDTO orderDTO =new OrderDTO();
orderDTO.setType(i+"");
orderDTO.setCode(i+"");
list.add(orderDTO);
}
System.out.println("humanSizeOf:"+RamUsageEstimator.humanSizeOf(list));
System.out.println("shallowSizeOf:"+RamUsageEstimator.shallowSizeOf(list));
System.out.println("sizeOf:"+RamUsageEstimator.sizeOf(list));

结果:

humanSizeOf:1.2 KB

shallowSizeOf:24

sizeOf:1280

林老师带你学编程https://wolzq.com

林老师带你学编程 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!