使用MAT分析java collection 使用情况

在之前的博客<那些分配但是未使用的内存>中,描述了如何使用SAP的内存分析工具去找出哪些内存分配给collection但是被浪费了。这可以快速减少内存使用。这篇文章将会给出这款工具的几个深入分析collection 使用的特性。

上一篇博客 很好地回答了“为什么对象持有如此多size为0 的对象?”。当然,另外一个问题是“对象持有的集合的大小是多少?“ 。更进一步问 ”对象持有的集合的使用率有多高?” 或者“对象持有的hash方法运行正常吗?”当把对象放入hashmap 是否会触发好多冲突?“ 这些都是常见的问题。这些问题的答案可以使程序员在实现某个特定需求时做出更好的选择。但是在设计开发阶段,这些问题很难找到明确的答案。但是这些问题稍后会得到解答。本文将基于heap dump ,使用MAT来分析。

检索浏览部分的集合集

在1.1.1版本的MAT中引入了Query Brower. Query Brower 可以很好对堆的边边角角进行各种不同的查询。查询集合集专注于分析Java集合和数组。下面将使用查询语句来尝试得到上述问题的答案。

下面从两个方面进行分析:

  • 查询heap dump 文件中某一个特定类型的对象,然后尝试分析它们来自哪儿。例如: 按照大小分析hashmap,然后分析什么对象持有size为1的hashmap。如何关注于整个堆,那么这种方法比较有效。
  • 另一个方法是先选出某些对象去分析,然后在集合集中去执行这些命令。比如: 找到匹配”my.own.compoment“模式 的类的retained set。在retained set 直方图中找出Hashmap 的行,然后执行一个查询去找出大小分配。这种方法对于在整个应用中找到某个特定的组件非常有帮助。

collections 多大? 哪些对象持有这些collections?

开始分析集合的大小。这有啥用呢?集合存储运行时产生的数据。开发时会考虑应该用哪些数据结构,初始时如何设定等问题。然而这些问题的答案并不确切。看heap dump文件中的现场有助于去证明何种假设是正确的。可以很容易的得知初始值是设置大了,还是设置小了。

看个例子。假如我是一个关心com.sap.memory.demo.* 组件占用内存状态的工程师,作为这个过程的一部分,我会更倾向分析我代码的性能。

首先,我会分析com.sap.memory.demo.* 类中每个实例的retaind set.通过这一步,会聚焦在跟我相关的实例上,摒弃heap dump文件中的其他对象。

然后过滤retained set直方图中的 ”.*HashMap.*“,然后按照集合集大小进行检索。

结果是一个表,第一列是集合大小,然后是对象的个数,然后是shallow size 和 retained size.

找到大小为0的集合,然后使用”immediate dominators“ 去找出谁持有这些集合。因为我只关注自己的内容,所以将会看到自己内容中的类。

可以看到我的类持有38000个hashmap中有25000个是没用的。大约18000个大小为0的hashmap 被我的InefficientDataStructure 类占用,剩余的被其他两个我的类使用。

对象持有的集合的填充率是多少?

对象填充率查询是跟我们上述讲到的查询非常类似。不同点是填充率仅适用于预分配空间的集合(比如ArrayList, HashMap等)并且提供了预分配使用了多少空间的详细信息。填充率介于0到1之间,用size/capacity 来计算。

当执行被”com.sap.memory.demo.*“ 占用的hashmap的填充率时,得到如下结果:

从结果中可以看到在38000个hashmap实例中有25000个是空的,12000个的填充率不足20%。

为了更好的理解,可以将上面两个查询组合起来执行。可以先执行”Collections Fill Ratio“然后执行”Collections Grouped By Size“。

对象持有的hash方法运行正常吗?

进行下一个话题-Hashmap和Hashtables中的冲突。一个糟糕的hash 方法的实现在查询时会严重恶化性能。最糟糕的情况是所有的元素返回同一个hash code,这样查起来就像查链表一样了。

然而,如果对象产生的hashcode 冲突较多,则会引起另一个问题。这在开发阶段是很难发现的。通过MAT结合heapdump 文件可以很容易地发现。尽管这个问题更像是性能问题,而不是内存问题,在MAT中提供了一个查询(Map Collision Ratio)可以解决这个问题。这个检索针对Hashmap(或者Hashtable) ,将他们按照冲突比例进行汇总。冲突率的计算会涉及hash table中所有冲突的条目。

再看个例子。化身一个性能专家,来分析另外一个同事的代码。并不会聚焦在某个特定的组件上,而是heap dump文件中的hashtable实例。

得到如下结果: 第一列是冲突率,第二列是对象数据等

可以看到有一个对象的冲突率介于80%到100%。

然后研究这个实例,查看所有位于这个冲突中的key值。

可以看到具体的key.这个可以很方便的定位到这位开发InefficientDataStructure.Key 类的同事的问题。

一个查看hashmap 内容的简单方法

”Hash Entries“查询提供了一个Map结构体内容的简便方式。这是在分析时我常用的操作。

如果按照引用去查看map条目并不是非常直白。首先,key values相互交叠。第二点是会有一些冲突使得阅读起来比较困难。看一下下面的例子这清晰吗?

通过对一个或者几个对象执行”Hash Entries“,【右键-java collection - hash entries】可以得到个表格

这看起来有些清晰了。当然可以通过context 菜单进行进一步的分析。

分析数组

对数组分析,可以使用如下两个功能

  • Arrays Grouped By Size - 对私有和对象数据起作用

  • Array Fill Ratio - 对私有对象无效,提供数组中非空元素的比例以及数组长度

总结

本文从方方面面分析了collection 的内存使用情况。当然也包括一些性能分析。对我日常工作非常有用。同事用了都说好。

但是我相信社区还有一些针对复杂问题分析的其他比较好的建议。欢迎给我提交一或者告诉我这些技巧。

参考文献

https://blogs.sap.com/2007/11/04/analyzing-java-collections-usage-with-memory-analyzer/

results matching ""

    No results matching ""