博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java heap space 问题查找
阅读量:6642 次
发布时间:2019-06-25

本文共 4395 字,大约阅读时间需要 14 分钟。

hot3.png

在项目开发上线的过程中,最近发现一个Dubbo服务隔7天左右就会出现以下问题:

Exception in thread "Timer-0" java.lang.OutOfMemoryError: Java heap space

 

(一开始使用findBugs进行扫描,并未扫描出可用结果)

首先,介绍一个免费开源分析dump的软件Memory Analyzer,下载地址如下所示:(同事介绍)

此工具需要依赖于dump,可以根据以下命令生成JVM的dump文件

ps aux | grep xxx 查询进程ID

jmap -dump:live,format=b,file=文件名.bin   进程ID

 

可将生成的dump文件下载到本地,使用Memory Analyzer打开其文件进行分析。

最好在进程刚刚开启时就生成一个dump文件,在服务使用一段时间后再生成一个dump文件,两个文件进行对比排除内存漏洞问题。

经过排查发现漏洞问题如下所示:

 

 发现问题出现在了JceSecurity的verificationResults 属性上,在verificationResults 属性中存在了过多的BouncyCastleProvider,随着应用的使用正在不断的增多,未被GC回收。

对javax.crypto.JceSecurity进行反编译查看代码发现verificationResults 是static 类属性,GC不会自动对其永久代进行回收。

对项目代码进行排查,发现项目中使用代码BouncyCastleProvider使用代码如下所示:

Java代码 

Cipher ci = Cipher.getInstance("RSA", new BouncyCastleProvider()); 

 

 发现是坑张的代码,在服务每次使用的时候都会重新创建一个BouncyCastleProvider用来进行初始化密钥的工具类。

Java代码

public static final Cipher getInstance(String paramString, Provider paramProvider)      throws NoSuchAlgorithmException, NoSuchPaddingException{      if (paramProvider == null) {        throw new IllegalArgumentException("Missing provider");      }      Object localObject1 = null;      List localList = getTransforms(paramString);      int i = 0;      String str = null;      for (Iterator localIterator = localList.iterator(); localIterator.hasNext(); ) {        Transform localTransform = (Transform)localIterator.next();        Provider.Service localService = paramProvider.getService("Cipher", localTransform.transform);        if (localService == null)          continue;        Object localObject2;        Object localObject3;        if (i == 0){          localObject2 = JceSecurity.getVerificationResult(paramProvider);          if (localObject2 != null) {            localObject3 = "JCE cannot authenticate the provider " + paramProvider.getName();              throw new SecurityException((String)localObject3, (Throwable)localObject2);          }          i = 1;        }        if (localTransform.supportsMode(localService) == 0) {          continue;        }        if (localTransform.supportsPadding(localService) == 0) {          str = localTransform.pad;        }        try{          localObject2 = (CipherSpi)localService.newInstance(null);          localTransform.setModePadding((CipherSpi)localObject2);          localObject3 = new Cipher((CipherSpi)localObject2, paramString);          ((Cipher)localObject3).provider = localService.getProvider();          ((Cipher)localObject3).initCryptoPermission();          return localObject3;        } catch (Exception localException) {          localObject1 = localException;        }      }      if (localObject1 instanceof NoSuchPaddingException) {        throw ((NoSuchPaddingException)localObject1);      }      if (str != null) {        throw new NoSuchPaddingException("Padding not supported: " + str);      }      throw new NoSuchAlgorithmException("No such algorithm: " + paramString, localObject1);    }  

 

可查看BouncyCastleProvider代码发现此类进行过特殊处理,每次new出的实例hashCode是相同的。又对JceSecurity.getVerificationResult方法代码进行了分析,代码如下所示:

Java代码 

static synchronized Exception getVerificationResult(Provider paramProvider){      Object localObject1 = verificationResults.get(paramProvider);      if (localObject1 == PROVIDER_VERIFIED)        return null;      if (localObject1 != null) {        return (Exception)localObject1;      }      if (verifyingProviders.get(paramProvider) != null)      {        return new NoSuchProviderException("Recursion during verification");      }Exception localException2;      try {        verifyingProviders.put(paramProvider, Boolean.FALSE);        URL localURL = getCodeBase(paramProvider.getClass());        verifyProviderJar(localURL);          verificationResults.put(paramProvider, PROVIDER_VERIFIED);        localException2 = null;          return localException2;      }      catch (Exception localException1){        verificationResults.put(paramProvider, localException1);        localException2 = localException1;        return localException2; } finally { verifyingProviders.remove(paramProvider); }    }  

查找到这里发现自己越来越矛盾,每次new出来的BouncyCastleProvider具有相同的hashCode,放在verificationResults 属性Map中怎么会越来越多,后一个应当会将前一个覆盖才对,怎么会导致内存溢出。

 

最终实在无头绪请教同事,发现一个verificationResults属性定义的居然是IdentityHashMap,此Map在存储类的时候并不是使用类的equals方法来判断是否Key已经存在,而是使用==来判断是否Key已经存在的。换句话说就是当两个对象不==那此Map就会将两个对象都存进去。

 

找到这里问题的解决方案就已经非常明了了,只要给BouncyCastleProvider定义成单例就可以了。

转载于:https://my.oschina.net/xiaoluobutou/blog/803671

你可能感兴趣的文章
[书目20130216]深入浅出WPF
查看>>
hibernate 一级缓存and 快照
查看>>
iPhone电池到底为何不耐用,真相让人意外
查看>>
真正实现多点触控索尼Xperia Touch试用
查看>>
如何在OpenStack中轻松部署MySQL应用
查看>>
VMware vSphere:架构解析及应用案例
查看>>
可视化探索卷积神经网络提取特征
查看>>
Docker-run
查看>>
一点资讯拆VIE回国;拼多多做跨境海淘;复宏汉霖首款生物类似药获批
查看>>
怎么设计IOS登录页面的跳转关系
查看>>
ecshop 漏洞如何修复 补丁升级与安全修复详情
查看>>
【三】Centos7.4 安装Docker
查看>>
拜腾与博世将在动力系统、驾驶员辅助等方面展开重点合作
查看>>
国资入场,P2P网贷平台星火钱包千万级A+轮融资
查看>>
windows server21012 r2 密钥
查看>>
北大发布新零售之城发展指数报告,上海超北京成榜首
查看>>
python urllib爬取网页编码问题
查看>>
JMS的常用方法
查看>>
隐私与机器学习,二者可以兼得吗?
查看>>
DNS原理概念详解
查看>>