文档章节

在命令行中使用Eclipse MAT工具

闪电
 闪电
发布于 2016/12/09 23:19
字数 880
阅读 15
收藏 0

最近应用在测试中出现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的元凶。

使用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的内存,
消息体太大了。

编写程序得到所需信息

你也可以引用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消息的内容解决了我的问题。

参考文档

  1. https://wiki.eclipse.org/MemoryAnalyzer/Extending_Memory_Analyzer
  2. http://javaforu.blogspot.jp/2013/11/analyzing-large-java-heap-dumps-when.html
  3. http://dr-brenschede.de/bheapsampler/
  4. http://www.techpaste.com/2015/07/how-to-analyse-large-heap-dumps/
  5. http://stackoverflow.com/questions/15977723/analyse-a-hprof-memory-dump-file-from-command-line-programmically
  6. http://wangjunyan.github.io/2014/12/02/export-android-bitmap-from-dump-file/

本文转载自:http://colobu.com/2015/08/12/using-Eclipse-Memory-Analyzer-by-the-command-line/

闪电
粉丝 75
博文 392
码字总数 6789
作品 0
海淀
技术主管
私信 提问
在Exchange 2003的03域改名

环境描述: Windows 2003AD+Exchange Server 2003 要求: 1 所有的DC必须是Windows Server 2003 2 所有的DC必须在线 3 Exchange Server必须为2003sp1或以上版本 4 域重命名操作在成员服务器上...

文弦
2009/06/17
0
0
使用adb工具访问sqlite数据库

使用adb工具访问sqlite数据库 Android Debug Bridge(ADB)是Android的一个通用调试工具,它可以更新设备或模拟器中的代码,可以管理预定端口,可以在设备上运行shell命令,我们知道android...

liuzs1208
2014/06/24
0
0
iOS开发最新之CocoaPods环境配置教程

CocoaPods简介 每种语言发展到一个阶段,就会出现相应的依赖管理工具,例如 Java 语言的 Maven,nodejs 的 npm。随着 iOS 开发者的增多,业界也出现了为 iOS 程序提供依赖管理的工具,它的名...

ziyuzhiye
2017/08/29
0
0
MySQL 命令行导出、导入Select 查询结果

MySQL 命令行导出、导入Select 查询结果 有的时候需要把在一张表中用 select 语句查询出来的结果保存到另一张结构相同的表中,可以有几种方法来实现: 方法一: 在命令行下使用一对SQL语句完...

xiaocao13140
2018/06/15
0
0
Bash 中使用控制运算符连接命令

在命令行中,使用控制运算符为复合命令添加逻辑。 经常会使用一些简单的复合指令,比如说在一个命令行中连接几个命令。这些命令使用分号分隔,表示一个命令结束。为了在一个命令行中创建一系...

作者: David Both
2018/12/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

优雅的关闭Spring Boot

优雅的关闭Spring Boot 1、实现 TomcatConnectorCustomizer 接口拿到Tomcat的连接获取 Tomcat连接池 2、实现 ApplicationListener<ContextClosedEvent> 监听服务器关闭事件,注册JVM钩子函数...

sowhat
今天
2
0
Python3-Web开发

简介 Web开发框架 什么是Web框架? Web应用程序框架或简单的Web框架表示一组库和模块,使Web应用程序开发人员能够编写应用程序,而不必担心协议,线程管理等低级细节。 virtualenv是一个虚拟...

wuxinshui
今天
3
0
使用技媒体实践编写发布博客

技媒体实践博客 CSDN OSChina 知乎 简书 思否 掘金 51CTO

晨猫
今天
2
0
Lucene

1、什么是全文检索 数据分类 我们生活中的数据总体分为两种:结构化数据和非结构化数据。 结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等。 非结构化数据:指不定长或无固...

榴莲黑芝麻糊
昨天
5
0
python到setuptools、pip工具的安装

python安装 基础开发库   apt-get install gcc  apt-get install openssl libssl-dev 安装数据库和开发库   apt-get install mysql-server libmysqld-dev python环境   下载地址...

问题终结者
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部