找出被空集合占用的内存

在平时的内存分析中发现有很大一部分集合对象在实例化后就从没有被使用过。集合类是我们平时使用最多的一种类,部分集合类在实例化时就会得到一部分空间(比如ArrayList、HashMap等)。这些空的集合实例虽然价值不大,但也有可能会浪费很多的内存空间。接下来演示一下如何去发现并解决这个问题。

案例

看一下下面这个MyValueStorage类的代码,在这个类里定义了三个ArrayList型的成员变量,并做了初始化操作。这个三个成员变量中standardValues将会被经常用到,specialValues偶尔会被用到,erroneousValues只有在极少数的情况下(比如发生异常)才会被使用到。

一个空的ArrayList默认初始化capacity是10,在32位操作系统上会占用80byte的内存空间,在64位操作系统上则会占用144byte。假使这个类在系统中被广泛的使用,在内存中有接近500 000个实例。那么在32位的系统中将会为specialValues和erroneousValues保留80 M的空间(在64位系统上是144MB)。一个应对的思路就是延迟初始化,即直到使用这些对象的时候才将之实例化,否则一直为null。当然我们需要做一些额外的工作,比如添加几个“if”语句以避免空指针异常的出现。

换个角度来考虑这个问题:如果对象的实例不多的话,那么为之进行优化的工作就是没有必要的。所以在着手优化之前,需要先弄清楚优化工作是否可以获取到明显的收益,在我们这个例子里就是是否可以显著地节省内存。

怎样找出未使用的集合实例

要找出未使用的集合实例可以遵循如下的步骤:

  1. 正常运行实例一段时间,根据线程ID获取heap dump文件;
  2. 使用Memory Analyzer中的OQL(Object Query Language)工具来查找大小是0且修改次数也是0的集合对象。即在我们获取dump文件之前,这些集合对象一直是空的且从未被修改过。

点击工具栏上的“OQL”按钮启动OQL工具:

oql pane

顾名思义,OQL类似于SQL语句。这里我们使用如下的语句检查是否有空的且从未被使用过的 ArrayList, HashMap和Hashtable实例:

点击工具栏的红色叹号按钮执行查询。注意一次执行一行。

要想了解更多关于OQL的细节可以参考官方文档。此外,这篇文也不错,先将就着看,有时间整理一下。

计算空集合占用的内存

OQL查询得到的结果是一个符合查询要求的对象列表。想知道这些对象一共占用了多少内存可以开启histogram(直方图)视图。

collect histogram

然后计算所有对象占用的内存总量(使用工具栏上的“Calculate Retained Size”按钮,也可以在右键菜单中找到相关项),在下图实例中是266.4KB,还不算大。

compute

这些空集合在哪儿

在确定了空集合的大小以后,如果发现有必要去做一些优化,那么接下来的事情就是找出谁制造了这些空集合。一个最快捷的方式就是使用右键菜单中的“Immediate Dominators”项。

immediate dominator

结果如下图:

immediate dominator detail

看到了这个结果以后相信会比较容易定位到需要优化的位置。对于我这个应用来说是然并卵——大部分都在框架上,幸好占用的空间不大,还不需要优化。

就这样!!

参考文档

########


发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据