mac下 内存分析工具mat安装,使用mat分析:内存溢出、内存泄漏,结合jstat、jmap等命令的使用

分析前提:

  • 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默认是单例的有冲突嘛)

分析总结:

  1. Spring中的服务默认是单例的,这句话无可厚非
  2. 例子中的list.add(new Runnable()),会不断的创建线程任务到list中,因为写法是new Runnable(),实际就会创建一个匿名内部类(如何判断的呢,MonitorServiceImpl$1,看这个字符串末尾的$1就代表是内部类的标志)
  3. 如果没有线程及时的去处理list中的任务,就会导致list中的任务越来越多,如果还没有限制List的容量大小,最终会导致占用的内存越来越多,观察gc信息也可以发现,fullgc越来越频繁,系统几乎一直在fullgc,导致系统几乎Hung死

由于示范的例子不是很好,可能无法很好的去描述问题,有疑问欢迎留言~

或者直接点击这个按钮:

可能这个对象最多的不一定占用内存最大,但我们知道java是引用的,可能他引用的对象超级多、大,可以进一步慢慢分析

发布了142 篇原创文章 · 获赞 345 · 访问量 45万+

猜你喜欢

转载自blog.csdn.net/zhengchao1991/article/details/103014931