一次生产系统经常假死的问题排查

阅读更多
系统经常出现假死情况,出发了自动重启脚本,基本每台机器每台都会重启一两次
在其中一台机器去掉自动重启脚本,在机器假死的时候
1. 使用jstat -gcutil 查看,发现jvm无法进行垃圾回收
2.尝试把内存从原来的4G增加到8G,其中新生代4G,
机器没有触发自动重启,但是运行一段时间经常响应缓慢
3 .再次使用jstat -gcutil查看,发现yangGc 十分频繁,fullGc完 old区占用内存50%左右,也就是有2G多没有被回收
4.使用 jmap -dump:format=b,file=xxx.hprof $pid 把内存dump下来,使用mat工具分析
一次生产系统经常假死的问题排查_第1张图片
4.1发现有410.3M内存空间比较可疑
使用Dominator Tree查看如下
一次生产系统经常假死的问题排查_第2张图片
FTPFile对象有412940个,估计是把远程FTP文件夹里面的所有的文件信息都读取了,用来判断文件是否存在。
经过对业务功能的了解,有一个功能需要读取别的系统的FTP文件,由于读取FPT文件太慢了,所以使用定时任务定期从别的系统FTP同步数据到本地作为本地文件缓存。
这里的同步实现方式有问题,需要从业务实现角度来解决。
4.2另外两个可疑的内存对象,一个是hibernate另一个是eache的,考虑到hibernate跟eache都有设计到缓存相关,暂时没有去分析。
 
5.第二天,系统没有自动重启,但是多次缓慢告警后自行恢复。
使用jstat -gcutil查看,发现yangGc 十分频繁,fullGc完 old区占用内存80%左右3G多
重新把内存dump下来,这次分析的结果如下:

一次生产系统经常假死的问题排查_第3张图片

5.1没有发现FTP相关的,但是hibernate跟ecache都增大了很多。
使用Dominator Tree查看如下

一次生产系统经常假死的问题排查_第4张图片

一次生产系统经常假死的问题排查_第5张图片
这下发现是hibernate的一级缓存,
查看hql语句
SELECT DISTINCT NEW org.openkoala.auth.application.vo.ResourceVO( r.identifier,'',r.name,r.desc) FROM Resource r JOIN r.authorizations auth JOIN auth.identity i WHERE i.id IN (:x10_, :x11_, :x12_, :x13_ ,......:x124948_) AND r.abolishDate>?2 and auth.abolishDate>?3
红色的.....省略的成千上万个
经过查阅资料在一篇博客中找到了思路: https://blog.csdn.net/dream_lixiang/article/details/77248292
QueryPlanCache会缓存sql,以便于后边的相同的sql重复编译,如果in后的参数不同,hibernate会把其当成不同的sql进行缓存,从而缓存大量的sql导致heap内存溢出。(拼凑sql也会导致这个情况出现)。
经过了一番代码的排查,果然是由于上述的原因引起的。
其中in里面的参数是查询出来的角色id
一次生产系统经常假死的问题排查_第6张图片
修改方式,新增一个不启用hibernate一级缓存的查询方法(对应这个业务场景来说,一级缓存可以说是多余的),
JPA API与hibernate API有一点点区别:
一次生产系统经常假死的问题排查_第7张图片
5.2 ecache确实使用占用堆内存,对外内,本地存在的,所以暂时不分析
5.3 既然都分析到这里,顺便分析一下172.3M的内存里面到底是什么
javax.crypto.JceSecurity,这个似曾相识的感觉
使用Dominator Tree查看如下
一次生产系统经常假死的问题排查_第8张图片
5.3.1查找到代码:
5.3.2首先对Cipher.getInstance("RSA", "BC")与
Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
这两个api区别
Cipher.getInstance("RSA", "BC") 会根据第二参数去查找一个已经注册的BouncyCastleProvider对象所以要先在静态块把BouncyCastleProvider对象注册进入
static {
if (Security
.getProvider(org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
 
}
Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());

一次生产系统经常假死的问题排查_第9张图片

 
一次生产系统经常假死的问题排查_第10张图片
 
每次都会往verifyingProviders里面put一个对象,verifyingProviders是static final的不会被垃圾回收
所以会导致内存泄露
5.3.4 解决办法
1) 使用静态块预先注册
static {
if (Security
.getProvider(org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
 
}
 
Cipher cipher = Cipher.getInstance("RSA", "BC");
2)
使用单例
private static org.bouncycastle.jce.provider.BouncyCastleProvider bouncyCastleProvider = null; public static synchronized org.bouncycastle.jce.provider.BouncyCastleProvider getInstance() { if (bouncyCastleProvider == null) { bouncyCastleProvider = new org.bouncycastle.jce.provider.BouncyCastleProvider(); } return bouncyCastleProvider; }
 
Cipher cipher = Cipher.getInstance("RSA", getInstance() );
  • 一次生产系统经常假死的问题排查_第11张图片
  • 大小: 12.6 KB
  • 一次生产系统经常假死的问题排查_第12张图片
  • 大小: 70.8 KB
  • 一次生产系统经常假死的问题排查_第13张图片
  • 大小: 13.7 KB
  • 一次生产系统经常假死的问题排查_第14张图片
  • 大小: 86.3 KB
  • 一次生产系统经常假死的问题排查_第15张图片
  • 大小: 13.7 KB
  • 一次生产系统经常假死的问题排查_第16张图片
  • 大小: 12.8 KB
  • 一次生产系统经常假死的问题排查_第17张图片
  • 大小: 8.1 KB
  • 一次生产系统经常假死的问题排查_第18张图片
  • 大小: 88.8 KB
  • 一次生产系统经常假死的问题排查_第19张图片
  • 大小: 2.8 KB
  • 一次生产系统经常假死的问题排查_第20张图片
  • 大小: 44.5 KB
  • 一次生产系统经常假死的问题排查_第21张图片
  • 大小: 27.5 KB
  • 一次生产系统经常假死的问题排查_第22张图片
  • 大小: 5.7 KB
  • 查看图片附件

你可能感兴趣的:(一次生产系统经常假死的问题排查)