MAT分析JAVA进程内存

MAT分析JAVA进程内存

jstatd + jvisualvm监视内存使用

一般情况下,如果服务端开了JMX端口,那么可以用jvisualvm对接堆jmx端口进行监控。但有时服务进程可能没有开jmx端口,那么就可以借助于jstatd进程来完成实现监控了。jstatd的作用是获取服务进程的运行信息,然后jvisualvm通过jmx端口(jstatd会启动jmx端口)获取远端服务进程的内存使用情况。

在目标服务器启动jstatd

在任意目录下创建一个文件,只要以.policy结尾,这里假设路径是/opt/jstad-config/jstatd.all.policy:

1
2
3
grant codebase "file:${java.home}/../lib/tools.jar"{
permission java.security.AllPermission;
};

以后端进程形式启动jstatd,默认开启jmx端口1099:

1
jstatd -J-Djava.security.policy=/opt/jstad-config/jstatd.all.policy -J-Djava.rmi.server.hostname=xx.yy.zz.aa &

然后可远程查看java进程(语法jps xx.yy.zz.aa)或使用jvisualvm连接到该服务器上查看需要监控的进程。

参考:https://blog.csdn.net/u013887008/article/details/84746610

在jvisualvm监控远程java进程

打开jvisualvm。

在左侧remote下添加远程服务器,hostname就按照上面启动jstatd进程时指定的hostname填写。

添加完成后,选中远程服务器节点,右键,选择“add jstatd connection“。填写jstatd使用的jmx端口及刷新间隔。

添加连接:

add jstatd conn

设置连接:

add jstatd conn

然后,可以在jvisualvm实时监控远端服务内存情况了。

说明:这种方法对远端服务器的资源消耗有多少,具体没测试过。可能需要注意一下。

深度分析java代码

代码结构

Entity类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.ajupiter;

import java.util.ArrayList;

public class Entity {
ArrayList<String> arrayList;
String name;
int age;

public Entity(String a, int b) {
name = a;
int len = 100;
arrayList = new ArrayList<String>(100);
for (int i = 0; i < len; i++) {
arrayList.add("thisi is no. " + i);
}
age = b;
}
}

产生OOME错误的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.ajupiter;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

//测试产生OOM错误,实际最大运行时间约30s,但一般会因为产生OOM错误提前结束
public class OOMProducer {

public void oom() throws InterruptedException {
int len = 30000;
Object[] array = new Object[len];
for (int i = 0; i < len; i++) {
String d = new Date().toString();
Entity p = new Entity(d, i);
Map<Integer, Entity> map = new HashMap<>();
map.put(i, p);
array[i] = p;
Thread.sleep(1);
}
}
}

主类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajupiter;

public class Main {

public static void main(String[] args) {
OOMProducer oomProducer = new OOMProducer();
try {
oomProducer.oom();
} catch (InterruptedException e) {
System.out.println("出现错误");
e.printStackTrace();
}
}
}

运行程序使产生OOME错误

使用javac 编译代码:

1
javac -d target src/**/*.java

然后cd到target目录,执行程序。因为我指定了最大堆大小为10M,所以程序运行到一段时间后报错,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  target java -Xmx10m -Xms10m -XX:+HeapDumpOnOutOfMemoryError com.ajupiter.Main
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid33714.hprof ...
Heap dump file created [14343715 bytes in 0.080 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.base/jdk.internal.misc.Unsafe.allocateUninitializedArray(Unsafe.java:1271)
at java.base/java.lang.invoke.StringConcatFactory$MethodHandleInlineCopyStrategy.newArray(StringConcatFactory.java:1633)
at java.base/java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(DirectMethodHandle$Holder)
at java.base/java.lang.invoke.LambdaForm$MH/0x0000000800063c40.invoke(LambdaForm$MH)
at java.base/java.lang.invoke.Invokers$Holder.linkToTargetMethod(Invokers$Holder)
at com.ajupiter.Entity.<init>(Entity.java:16)
at com.ajupiter.OOMProducer.oom(OOMProducer.java:15)
at com.ajupiter.Main.main(Main.java:9)

如果是在IDEA中运行,也可以在菜单Run下的Run/Debug Configurations对话框,添加VM选项-Xmx10m -Xms10m -XX:+HeapDumpOnOutOfMemoryError

add vm option

在运行过程中,可以借助于VisualVM观察该程序实际使用的内存情况。

使用MAT分析

对上述产生的java_pid33714.hprof文件,使用MAT(Eclipse Memory Analyzer Tool)分析。

显示了具体的内存泄漏情况。

mat analyze

有一篇文章对内存泄漏有详细分析,个人觉得写得还是很不错的。见https://blog.csdn.net/rachel_luo/article/details/8992461。上面的代码案例也基本参考了这篇博文。

提示:如果产生的dump文件(.hprof)比较大,记得修改MAT的最大堆大小。

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2021 Johnny Li
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信