前言:上篇【jvm】面试官求你别再问了-死锁,内存溢出及泄漏如何监控及解决(上) 介绍了通过基础命令的方式来监控及解决死锁,内存溢出及泄露问题,本篇主要介绍通过工具监控及解决这些问题,毕竟工欲善其事,必先利其器,好的工具可以让监控和解决问题事半功倍.好的监控工具有很多,中大型公司基本都有自己的监控工具和体系,本篇主要介绍开源的工具,常见的开源监控工具有jdk自带的jconsole,jvisovm以及Jprofiler等,Jdk自带的监控工具我在之前的博客中有总结(https://blog.csdn.net/lovexiaotaozi/article/details/82862196?spm=1001.2014.3001.5501),本篇的主角是Jprofiler.
目录
1.工具监控的优劣
2.Jprofiler简介
3.Jprofiler安装及IDEA集成
4.Jprofiler如何监控并解决死锁
5.Jprofiler如何监控并解决内存溢出 (建议直接跳过)
1.栈溢出stackOverflow
2.堆溢出
3.永久代溢出(jdk8以后为MetaSpace元空间,堆外内存)
6.Jprofiler如何监控及解决内存泄露(划重点)
7.Jprofiler还能做哪些事
8.总结
1.工具监控的优劣
优:高效,直观,使用简单,门槛低,效果好,小白也能轻松上手并实现大师级(吹牛皮)的效果...
劣:需要额外的安装及学习成本,如果与应用服务器直连的话,会多暴露端口,带来风险及少量性能开销(不过好在都有相应的解决方案)
2.Jprofiler简介
Jprofilers是针对JAVA开发的剖析工具,通过它可以监控到java程序的内存,CPU,线程,GC,锁等进行监控和分析,功能非常强大,不过要收费,好在有10天免费试用期,有需要的也可以购买正版,或者...你懂的
3.Jprofiler安装及IDEA集成
本篇仅介绍Jprofiler的安装及如何Intelij IDEA中的集成,如有用Elicplise的同学这边也不建议你另找教程,建议尝试用IDEA.
这里我的IDEA版本是2020.2.2,选择的Jprofiler版本是12.0(早期的版本是纯英文的,12.0支持中文了,对新手比较友好,不过无关紧要,英文一样容易上手,主要考虑是否与IDEA插件兼容即可)
安装主要分为两步:
第一步:进入Jprofiler官网下载 -> Jprofiler 版本这边建议选择最新的或者次新的(前提是你的IEDA版本也比较新)
第二步:打开IDEA->File->settings->plugins->marketplace->搜索 Jprofiler

插件安装后,在IDEA工具栏可以找到如图所示的两个图标,点击箭头指的那个,进行配置,主要就是配置个路径,让插件能找到第一步中下载好的Jprofiler:

我是在IDEA安装目录的插件目录下,新建了个Jprofiler文件夹,然后把刚刚下好的Jprofiler拖到里面了,唯一值得注意的是需要重命名,把带版本号的Jprofiler_windowsxxx.exe重命名成Jprofiler.exe,否则你选不中,Idea插件对它的名称作了死限制...

选中后然后同意安装协议,一路next确认就好了,最后选试用10天激活,如果你有秘钥可以选永久激活,至此就安装好了,然后可以通过该按钮启动应用

启动后就可以获取到启动应用的各种信息了,当然你也可以连接远程服务器上的java应用,或者通过dump出来的文件分析等:

4.Jprofiler如何监控并解决死锁
先写了一段死锁代码,用于演示死锁:
public static void main(String[] args) {
Object a = new Object();
Object b = new Object();
Thread t1 = new Thread(()->{
synchronized (a){
try {
System.out.println("已获取到a锁,尝试获取b锁===>");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){
System.out.println("尝试获取b锁...");
}
}
});
Thread t2 = new Thread(()->{
synchronized (b){
try {
System.out.println("<===已获取到b锁,尝试获取a锁");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a){
System.out.println("尝试获取a锁...");
}
}
});
t1.start();
t2.start();
}
通过Jprofiler启动这段代码,然后可以在锁菜单界面直接看到当前锁的状态图:

然后可以在当前Monitor监视器设置一个观察的阈值,如果项目中的锁比较多,可以把超过一定时间的锁给筛选出来,进一步观察是否死锁,因为有些长时间阻塞的锁也可能是饿锁

然后针对筛选出来的几个可能是死锁的进行深入分析,首先可以看到这些锁的类型是处于阻塞的,然后可以在任意一个上面点击鼠标右键,选择在堆遍历器中显式所选内容:

然后在对遍历器中点击图表,就可以看到这个锁所在的代码位置,通过此工具就可以快速定位到一些可能出现死锁的代码位置,然后看一看代码就知道到底有没有死锁,还是饿锁了

通常来说产生死锁的原因主要有:
①没有设置锁的失效时间,然后代码里可能因为异常没有处理,锁没有在finaly代码块中释放导致其它线程无法获取锁而陷入等待;
②类似上图这种,锁互相竞争,线程t1持有A锁尝试获取B锁,线程t2持有B锁尝试获取A锁,谁也不让谁,互相干等着,这种情况要注意加锁的顺序,如果t1线程是先获得了A锁,再去获取B锁,然后t2线程去尝试获取A锁,再获取B锁,按这样的顺序就不会产生死锁.
③另外就是线程由线程优先级产生的饿锁,饿锁不算死锁,但也要引起重视,否则同样会造成资源浪费,可以通过JUC提供的公平锁解决.
按照上面的思路,去排查对应的代码,基本上死锁都能被解决.
5.Jprofiler如何监控并解决内存溢出 (建议直接跳过)
内存溢出其实比较好监控也比较好解决,用Jprofiler有点大材小用了,毕竟内存溢出可以在应用的日志中直接看到,稍微大点的公司也都有自己的一套监控工具,这里仅作简单介绍.
内存溢出主要分为三类,针对三种,分别有不同的解决方案
1.栈溢出stackOverflow
public static void main(String[] args) {
Test test = new Test();
test.recursion();
}
private void recursion(){
recursion();
}

栈溢出最容易出现在递归中,如果递归没有出口,或者递归的太深了,会容易爆栈,解决方案就是review报错位置的代码,看看是否因为代码写的有问题,导致递归找不到出口,一直递归下去... 如果代码没什么问题,就是需要很深的栈调用才能得出结果,这种极端情况可以适当调大调用线程的栈大小,或者将递归代码改为循环体去解决(推荐).

2.堆溢出
创建过多的对象导致的堆空间溢出
public static void main(String[] args) {
List