在命令行中使用 Eclipse MAT 工具

最近应用在测试中出现Out Of Memory的问题, 通过jmap查看,发现JVM heap全用满了。


有很多工具可以查看JVM堆的信息, 收费的比如JProfiler, YourKit,免费的如Oracle JDK自带的visualvm, jhat和Eclipse MAT。


这个应用安装在一台AWS上,没有图形界面, 内存也比较小,想通过VNC远程桌面启动visualvm或者MAT不可能,通过jhat分析dump出来的snapshot(大约4.3G)也很慢,半天没有分析完毕,这种办法也放弃。


最后通过MAT的命令行工具分析了dump出来的snapshot,查找到OOM的元凶。


1 使用MAT命令行工具


首先通过jstat或者jmap查看heap信息,比如通过jmap查看:


JVM version is 25.31-b07

using thread-local object allocation.

Parallel GC with 4 thread(s)

Heap Configuration:

   MinHeapFreeRatio         = 0

   MaxHeapFreeRatio         = 100

   MaxHeapSize              = 4294967296 (4096.0MB)

   NewSize                  = 1431306240 (1365.0MB)

   MaxNewSize               = 1431306240 (1365.0MB)

   OldSize                  = 2863661056 (2731.0MB)

   NewRatio                 = 2

   SurvivorRatio            = 8

   MetaspaceSize            = 21807104 (20.796875MB)

   CompressedClassSpaceSize = 1073741824 (1024.0MB)

   MaxMetaspaceSize         = 17592186044415 MB

   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:

PS Young Generation

Eden Space:

   capacity = 482344960 (460.0MB)

   used     = 468288384 (446.5946044921875MB)

   free     = 14056576 (13.4053955078125MB)

   97.08578358525816% used

From Space:

   capacity = 278921216 (266.0MB)

   used     = 0 (0.0MB)

   free     = 278921216 (266.0MB)

   0.0% used

To Space:

   capacity = 477102080 (455.0MB)

   used     = 0 (0.0MB)

   free     = 477102080 (455.0MB)

   0.0% used

PS Old Generation

   capacity = 2863661056 (2731.0MB)

   used     = 2863365080 (2730.7177352905273MB)

   free     = 295976 (0.28226470947265625MB)

   99.98966441927965% used

12340 interned Strings occupying 1051736 bytes.


最多的类的实例:


num     #instances         #bytes  class name

----------------------------------------------

   1:      21606534     1530253752  [C

   2:      21606239      518549736  java.lang.String

   3:      19198980      460775520  scala.collection.immutable.ListSet$Node

   4:       4568546      109645104  scala.collection.immutable.HashSet$HashSetCollision1

   5:        103739       63212992  [B

   6:       1487034       53464560  [Lscala.collection.immutable.HashSet;

   7:       1487034       35688816  scala.collection.immutable.HashSet$HashTrieSet

   8:       1350368       32408832  scala.collection.immutable.$colon$colon

   9:       1090897       26181528  scala.collection.immutable.HashSet$HashSet1

  10:        200035       17603080  akka.actor.ActorCell

  11:        100536        8042880  java.lang.reflect.Constructor

  12:        500026        8000416  scala.runtime.ObjectRef


从分析来看猜测是akka actor mailbox里面的字符串消息太多了。


既然没有办法图形化启动visualvm和MAT,那么就使用MAT文件夹下的ParseHeapDump.sh, 特别适合分析大堆的信息。


首先你需要修改MemoryAnalyzer.ini中的Xmx值,确保有充足的硬盘空间(至少dump文件的两倍)。


然后运行


./ParseHeapDump.sh heap.bin org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components


会得到suspects, overview和top_components三个视图的信息。



有问题的对象


可以看到akka.dispatch.Dispatcher$$anon$1一个实例占用了2.4GB的内存,这就是罪魁祸首。这其实是akka dispatcher的mailbox中的java.util.concurrent.ConcurrentLinkedQueue,每个Node占用了81M的内存,

消息体太大了。


2 编写程序得到所需信息


你也可以引用MAT的类,得到heap dump中的信息, 因为MAT使用Eclipse RCP框架, 基于osgi架构,使用起来不太方便,所以你可以别人抽取出来的MAT库,如https://bitbucket.org/joebowbeer/andromat,

然后实现一个命令行程序,比如下面的例子就是输出所有的字符串的值:


package com.colobu.mat;

import org.eclipse.mat.SnapshotException;

import org.eclipse.mat.parser.model.PrimitiveArrayImpl;

import org.eclipse.mat.snapshot.ISnapshot;

import org.eclipse.mat.parser.internal.SnapshotFactory;

import org.eclipse.mat.snapshot.model.IClass;

import org.eclipse.mat.snapshot.model.IObject;

import org.eclipse.mat.util.ConsoleProgressListener;

import org.eclipse.mat.util.IProgressListener;

import java.io.File;

import java.io.IOException;

import java.util.Collection;

import java.util.HashMap;

public class Main {

    public static void main(String[] args) throws SnapshotException, IOException {

            String arg = args[args.length - 1];

        String fileName = arg;

        IProgressListener listener = new ConsoleProgressListener(System.out);

        SnapshotFactory sf = new SnapshotFactory();

        ISnapshot snapshot = sf.openSnapshot(new File(fileName),

                new HashMap<String, String>(), listener);

        System.out.println(snapshot.getSnapshotInfo());

        System.out.println();

        String[] classNames = {"java.lang.String"};

        for (String name : classNames) {

            Collection<IClass> classes = snapshot.getClassesByName(name, false);

            if (classes == null || classes.isEmpty()) {

                System.out.println(String.format("Cannot find class %s in heap dump", name));

                continue;

            }

            assert classes.size() == 1;

            IClass clazz = classes.iterator().next();

            int[] objIds = clazz.getObjectIds();

            long minRetainedSize = snapshot.getMinRetainedSize(objIds, listener);

            System.out.println(String.format("%s instances = %d, retained size >= %d", clazz.getName(), objIds.length, minRetainedSize));

            for (int i = 0; i < objIds.length; i++) {

                IObject str = snapshot.getObject(objIds[i]);

                String address = Long.toHexString(snapshot.mapIdToAddress(objIds[i]));

                PrimitiveArrayImpl chars = (PrimitiveArrayImpl) str.resolveValue("value");

                String value = new String((char[]) chars.getValueArray());

                System.out.println(String.format("id=%d, address=%s, value=%s", objIds[i], address, value));

            }

        }

    }

}


基本上使用ParseHeapDump.sh已经得到了我所需要的结果,优化akka actor消息的内容解决了我的问题。


3 参考文档


  • https://wiki.eclipse.org/MemoryAnalyzer/Extending_Memory_Analyzer


  • http://javaforu.blogspot.jp/2013/11/analyzing-large-java-heap-dumps-when.html


  • http://dr-brenschede.de/bheapsampler/


  • http://www.techpaste.com/2015/07/how-to-analyse-large-heap-dumps/


  • http://stackoverflow.com/questions/15977723/analyse-a-hprof-memory-dump-file-from-command-line-programmically


  • http://wangjunyan.github.io/2014/12/02/export-android-bitmap-from-dump-file/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值