内存分析工具MAT的使用入门
MAT工具介绍
工欲善其事必先利其器,学会使用工具也是一种本领。本篇文章就把自己之前工作中用到的一个内存分析工具给大家介绍下。
内存分析工具MAT(Memory Analyzer Tool)是一款 JVM 的内存分析工具,在实际的工作中可以帮助我们解决生成上内存占用过高等问题。
我之前用 MAT 是在 eclipse上使用,前者是后者的一个插件。后来换到 IDEA 才知道原来 MAT 也有独立的可运行版本。它的下载地址如下:
http://www.eclipse.org/mat/downloads.php
测试代码
我们先准备一段简单的代码,这个代码会导致 JVM 堆内存溢出,方便我们演示 MAT 的效果。
public static void main(String[] args) throws InterruptedException {
Map<String,Tom> map = new HashMap<String,Tom>();
int counter = 1;
while(true) {
Thread.sleep(10);
Tom tom = new Tom();
String [] friends = new String[counter];
for (int i = 0; i < friends.length; i++) {
friends[i] = "friends"+i;
}
tom.setAge(counter);
tom.setName("tom"+counter);
tom.setFriends(friends);
map.put(tom.getName(),tom);
if(counter%100==0)
System.out.println("put"+counter);
counter++;
}
}
很好理解的一段代码,一个无限循环,不断的往map里添加名为Tom的对象,而且每次循环new出来的对象的friends属性还在不断的扩大。
然后我们使用启动下面这个启动参数运行代码,
-Xms200m -Xmx200m -XX:+HeapDumpOnOutOfMemoryError
参数指定了堆内存大小是200m,这个大小我们的测试代码很快就会用完,然后报错。
启动代码,运行一段时间后报错如下,
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to java_pid1398.hprof ...
Heap dump file created [239632332 bytes in 0.865 secs]
从这个报错我们可以获取几个信息,首先是错误类型是内存溢出,原因是超出了GC的限制。
其次,我们看到程序出错时的内存快照 dump 到了一个名为 java_pid1398.hprof 的文件中了。这个文件就是可以用于 MAT 工具分析的dump文件。
除了上面的通过 -XX:+HeapDumpOnOutOfMemoryError 参数来dump内存文件之外, 还可以通过 jmap 命令来导出的内存快照。这里不做详述了。
内存分析
我们现在根据 MAT 的分析,从几个维度来分析下代码中的问题。
MAT 工具打开前面的 dump 文件,会先看到下面这种图,
从预览图,可以看到有个应用占用了总的堆内存的大部分,高达184M(程序运行分配的堆内存是200M)。说明这个应用肯定有问题,值得我们继续往下分析。
我们先看看工具给我们的一个判断,找到 Leak Suspects,点击去。
从描述上看到,主线程有个本地变量占用了很大内存,这个变量是 HashMap 的实例。
哈哈,根据上面的代码,不得不说 MAT 还是很牛叉的,对于内存泄漏点定位的很准确。
不过有时候,我们还是需要手动分析下我们还是回到之前的预览页面,找到 Histogram 点进去,如下图:
shallow heap 指的是对象自身占用的内存大小,不包括它引用的对象。 针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。当然这里面还会包括一些java语言特性的数据存储单元。 针对数组类型的对象,它的大小是数组元素对象的大小总和。
我们看到排在前面的是占用内存比较多的, char[] 这个明显是String里引用造成的(string里用char[] 存储数据),我们之间来看String是被谁引用的,
很清晰,Tom对象的friends属性消耗了很多内存。
这里说明下,
- with incoming references 表示的是 当前查看的对象,被外部应用
- with outGoing references 表示的是 当前对象,引用了外部对象
同理,我们可以继续分析下面几个类,最终都会定位到内存吃紧的原因是 hashmap 里短时间塞了大量的 Tom 对象撑爆了内存。
参考:
https://www.cnblogs.com/hanlinhu/p/10174185.html
- Webpack中hash与chunkhash的区别,以及js与css的hash指纹解耦方案
- RPC原理及实现
- RMI原理及实现
- webpack多页面开发与懒加载hash解决方案
- 前后端分离和模块化-58到家微信首页重构之路
- Node.js建站笔记-使用react和react-router取代Backbone
- 协同过滤推荐算法Java代码实现
- Nginx模块之Filter解析
- Nodejs建站笔记-注册登录流程的简单实现
- 前端工程化-构建
- [翻译]开发Silverlight 2.0的自定义控件
- LINQ to SQL 辅助工具
- Nginx模块之Upstream解析
- Nginx之如何编写Handler模块
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- String - 242. Valid Anagram
- String - 290. Word Pattern
- String - 205. Isomorphic Strings
- String - 345. Reverse Vowels of a String
- String - 186. Reverse Words in a String II
- String - 151. Reverse Words in a String
- String - 344. Reverse String
- String - 383. Ransom Note
- String - 58. Length of Last Word
- String - 14. Longest Common Prefix
- String - 28. Implement strStr()
- Subsequence - 187. Repeated DNA Sequences
- Subsequence - 392. Is Subsequence
- parentheses - 241. Different Ways to Add Parentheses
- parentheses - 32. Longest Valid Parentheses