分析前提:
- mat的安装详情:https://blog.csdn.net/qq_34599254/article/details/82685110
https://www.cnblogs.com/trust-freedom/p/6744948.html - 安装后,再具体使用过程中,发现我dump的文件有4个G,而mat默认的配置是1个G,因此需要修改默认配置,修改流程如下:https://blog.csdn.net/freshfishfish/article/details/81585369
- jps命令查询进程号
-
jmap -histo pid | head -n20 查看TOP20对象分布,这个直接展示在屏幕上,也可打印到文件 jmap -histo:live [pid] >a.log
-
jstat -gc pid 3000:每隔三秒打印一次gc信息,运行结果中:
O:old代已使用的占当前容量百分比 ,YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
-
jmap -dump:format=b,file=/home/admin/logs/heap.hprof pid dump出系统的堆栈详情,便于用mat工具分析
-
匿名内部类的含义
-
Spring创建的实例,默认都是单例的
真实的代码实例不方便展示给大家,这里写一个简单的demo,方便大家感知下使用了内部类的一些问题:
//定义一个Sping的服务,有个test方法,作用就是往list里面add线程
@Component
public class MonitorServiceImpl implements MonitorService {
private static List<Runnable> list = new ArrayList<>();
@Override public void test(final GnssDeviceListInfo info) {
list.add(new Runnable() {
@Override public void run() {
info.getDeviceType();
}
});
}
}
//test方法就是往List队列中加线程任务(new Runnable这种写法就是匿名内部类)
//模拟一个不断调用该服务方法的restful接口,方便用Postman调用:
@GetMapping("/test")
public queryAllInServiceDevice(){
while(true){
Thread.sleep(1);
monitorService.test(new GnssDeviceListInfo());
}
}
项目启动后调用/test,就会触发test服务方法,运行一段时间后,发现系统非正常运行了,可以有很多指标,这里不说了,我们直接分析:
第0步:jps命名找服务进程号
第一步:观测GC信息
执行命名jstat -gccause 24203 3000,观测gc,发现:
第二步:用jmap命令打印出前30个耗费资源最多的对象:
这个例子写的太简单,直接可以看出来,假设我们看不出来,需要使用mat工具来排查,需要先dump实时的堆栈信息:
第三步:获取dump堆栈:jmap -dump:format=b,file=/Users/chao.zheng/heap.hprof 24203
第四步:根据mat分析生成的heap.hprof 文件:
分析第一个problem suspect 1
点击这个案例,用树形结构展示,方便看:
发现MonitorServiceImpl创建了大量的实例(其实上面的jmap -histo命令直接就可以看出来了(这里只是为了演示一些操作,读者可不必在意啊),有接近100万个对象创建出来了,这不是与大家掌握的知识:Spring默认是单例的有冲突嘛)
分析总结:
- Spring中的服务默认是单例的,这句话无可厚非
- 例子中的list.add(new Runnable()),会不断的创建线程任务到list中,因为写法是new Runnable(),实际就会创建一个匿名内部类(如何判断的呢,MonitorServiceImpl$1,看这个字符串末尾的$1就代表是内部类的标志)
- 如果没有线程及时的去处理list中的任务,就会导致list中的任务越来越多,如果还没有限制List的容量大小,最终会导致占用的内存越来越多,观察gc信息也可以发现,fullgc越来越频繁,系统几乎一直在fullgc,导致系统几乎Hung死
由于示范的例子不是很好,可能无法很好的去描述问题,有疑问欢迎留言~
或者直接点击这个按钮:
可能这个对象最多的不一定占用内存最大,但我们知道java是引用的,可能他引用的对象超级多、大,可以进一步慢慢分析