在平时的内存分析中发现有很大一部分集合对象在实例化后就从没有被使用过。集合类是我们平时使用最多的一种类,部分集合类在实例化时就会得到一部分空间(比如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”语句以避免空指针异常的出现。 换个角度来考虑这个问题:如果对象的实例不多的话,那么为之进行优化的工作就是没有必要的。所以在着手优化之前,需要先弄清楚优化工作是否可以获取到明显的收益,在我们这个例子里就是是否可以显著地节省内存。 怎样找出未使用的集合实例 要找出未使用的集合实例可以遵循如下的步骤: 正常运行实例一段时间,根据线程ID获取heap dump文件; 使用Memory Analyzer中的OQL(Object Query Language)工具来查找大小是0且修改次数也是0的集合对象。即在我们获取dump文件之前,这些集合对象一直是空的且从未被修改过。 点击工具栏上的“OQL”按钮启动OQL工具: 顾名思义,OQL类似于SQL语句。这里我们使用如下的语句检查是否有空的且从未被使用过的 ArrayList, HashMap和Hashtable实例: 点击工具栏的红色叹号按钮执行查询。注意一次执行一行。 要想了解更多关于OQL的细节可以参考官方文档。此外,这篇文也不错,先将就着看,有时间整理一下。 计算空集合占用的内存 OQL查询得到的结果是一个符合查询要求的对象列表。想知道这些对象一共占用了多少内存可以开启histogram(直方图)视图。 然后计算所有对象占用的内存总量(使用工具栏上的“Calculate Retained Size”按钮,也可以在右键菜单中找到相关项),在下图实例中是266.4KB,还不算大。 这些空集合在哪儿 在确定了空集合的大小以后,如果发现有必要去做一些优化,那么接下来的事情就是找出谁制造了这些空集合。一个最快捷的方式就是使用右键菜单中的“Immediate Dominators”项。 结果如下图: 看到了这个结果以后相信会比较容易定位到需要优化的位置。对于我这个应用来说是然并卵——大部分都在框架上,幸好占用的空间不大,还不需要优化。 就这样!! 参考文档 Memory For Nothing:http://scn.sap.com/people/krum.tsvetkov/blog/2007/08/02/memory-for-nothing Analyzing a Heap Dump Using OQL:http://visualvm.java.net/oqlhelp.html ########
[阅读更多...]-
找出被空集合占用的内存
-
使用Memory Analyzer分析内存泄漏
概述 检测内存泄漏通常采用的方式是检查内存中某些对象的数量是否存在单调递增的现象。这可以通过“在线”实时监控分析的方式或者比较不同时段的内存快照来实现。然而实时监控的方案通常并不可行,尤其是在生产环境上,得考虑到因此导致的性能消耗;并且内存泄漏的产生通常也是非常偶然的,只有在某些特定条件下才会发生。这篇文章将介绍一些使用MAT发现内存泄漏的技巧。 准备工作 首先一定要有足够的数据,这里指的是heap dump文件。可以对JVM进行配置,以实现一发生OutOfMemoryError就自动生成Heap Dump文件。 第二步就是让内存泄漏问题清楚地暴露出来从而容易被捕捉到。这里有一个技巧:试着调整一下应用运行时的最大堆内存,调整到比应用正常运行所需的内存大一些就可以了(建议是一次Full GC后剩余内存的两倍大小)。即使不知道应用运行时到底需要多大的内存,加大堆内存也不是一个坏主意(有时可能真的就是内存不够用了,而非是发生了内存泄漏)。这里不讨论分配给一个应用太大的堆内存是好还是坏——这里只是将调整内存作为故障排除的一个临时方案。调整内存后我们会得到什么呢:如果确实存在内存泄漏,和内存泄漏有关的对象的大小估计会占到堆内存的一半(如果当时设置的是两倍),此时再找导致内存泄漏的原因应该就比较容易了。 案例一 现在假设我们已经做好了配置,然后某一天发生了MMO错误并生成了一个相当大的heap dump文件。接下来该怎么做呢?说实话,接下来要做的事情非常简单。 首先使用MAT打开这个heap dump文件。如果文件非常大的话,第一次打开可能会需要等一段时间,之后再打开这个文件就会非常快了,因为首次打开的时候已经完成了对文件的解析。现在开始尝试找出到底是谁蚕食了我们的内存。点击工具栏上的按钮进入Dominator tree视图: 这里会看到首页的对象图以一个树的形式展现出来。这个树里展示了对象、依赖、它们之间的引用关系以及其他。这里不会详细介绍这个树背后的全部细节,只是说一下两个重要的指标: 在树的最顶端(依Retained Heap Size排序)可以看到内存中最大的对象; 最大的对象就是占用内存最多的对象,它在树中的子节点都是被该对象直接或间接引用的对象(这意味着当这个对象被回收的时候它的子节点对象也会被回收)。 一般发生内存泄漏的时候,都会直接锁定那个最大的对象。接下来一步步接近真相:展开最大对象的子树,试着找到retained size最接近最大对象的子节点(通常是一个数组或者集合)。就是这么简单,我们找到了内存泄漏的元凶。如果还想继续探索下去的话,可以尝试探索更深的子节点。 看一下展开后的结果: 下一件事就是找到内存泄漏的对象到GCRoots的引用链。选中内存泄漏对象,右键菜单中选择“Paths from the GC roots -> without weak and soft references”即可: 在“Paths from the GC Roots”可以看到我们选中的对象到GCRoot的路径,最顶端是我们选中的目标,最下方是GCRoots。选的样本不好,估计到GCRoot还得展开好久: 换了一个样本,这下清晰多了: 案例二 如果所有的问题都能像上面的案例那样容易解决就太好了。有时候仅仅看一次dominator tree是远远不够的。看看下图的案例:这里也提供了足够多的内存让内存泄漏对象去生长,也打开一个覆盖了所有对象的dominator tree,其中就包括内存泄漏对象。但是能看到内存泄漏对象么?在案例一中,所有小的内存泄漏对象都被一个巨大的根对象引用,但是有时候这些相对较小的内存泄漏对象就直接在dominator tree的顶级节点上。尽管这些小的内存泄漏对象数量很多,但是每个对象的Retained Size都比较小,因此不会排在前面。 此时点击工具栏中的“Group by class”按钮会有很大的帮助: 点击按钮后,我们可以看到同一组对象Size的总和,此时再找内存泄漏的原因是不是会更容易一些: 其他 附上测试程序: 模拟时使用的虚拟机参数: 参考文档 Finding Memory Leaks with SAP Memory Analyzer Shallow heap & Retained heap
[阅读更多...] -
使用MAT找出重复引用的jar或类
这篇文章将介绍一种处理应用中的类(或库)的重复引用以及版本冲突问题的解决方案。这在应用集成阶段非常有效。适用于如下案例: NoClassDefFoundError:在classpath中有两个不同版本的同名类; 集成大型产品:检查同一个版本的jar包是否被重复引入,减少内存占用。 准备工作 安装Memory Analyzer Tools,在eclipse上搜mat插件、百度搜索、谷歌搜索都可以。 获取heap dump文件,参考这里:《关于Heap Dump》 步骤 使用MAT打开dump文件,执行Open Query browser->Java basics->Duplicated classes,如下图: 现在我们可以看到所有重复的类及相关的类加载器了: 需要注意的是:相关的类必须是被加载过才能找得到。 一个小技巧 既然我们已经做到这里了,顺便给您展示一下另外一个小tip。通过Inspector视图,可以看到被加载的类具体是在哪个jar包里。举例说,如果一个重复的类是被URLClassloader加载的,只需按以下的步骤执行: 选择目标类; 通过类属性页签进入Inspector视图; 右键点击“_context”属性; 最后点击“Go Into”。 在弹出的窗口窗口中,可以看到属性“_war”,然后就是被加载的类的位置了: ###### 本文译自下文:http://community.bonitasoft.com/blog/effective-way-fight-duplicated-libs-and-version-conflicting-classes-using-memory-analyzer-tool
[阅读更多...]