今天给大家介绍一个对象内存计算神奇。jvm内存溢出的时候,我们可以通过很多方法查看原因,很多时候也需要查看具体是哪一个大对象导致内存溢出。
这里要介绍的是lucene提供的专门用于计算堆内存占用大小的工具类:RamUsageEstimato
maven坐标:
<!--加载内存查看工具--> <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:
//计算指定对象及其引用树上的所有对象的综合大小,单位字节 long RamUsageEstimator.sizeOf(Object obj) //计算指定对象本身在堆空间的大小,单位字节 long RamUsageEstimator.shallowSizeOf(Object obj) //计算指定对象及其引用树上的所有对象的综合大小,返回可读的结果,如:2KB String RamUsageEstimator.humanSizeOf(Object obj)
点评:使用该第三方工具比较简单直接,主要依靠JVM本身环境、参数及CPU架构计算头信息,再依据数据类型的标准计算实例字段大小,计算速度很快,另外使用较方便。如果非要说这种方式有什么缺点的话,那就是这种方式计算所得的对象头大小是基于JVM声明规范的,并不是通过运行时内存地址计算而得,存在与实际大小不符的这种可能性。
具体demo案例:
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