查看 java heapspace_Java heap space 问题查找

查看 java heapspace_Java heap space 问题查找在项目开发上线的过程中 最近发现一个 Dubbo 服务隔 7 天左右就会出现以下问题 Exceptionint Timer 0 java lang OutOfMemoryE Javaheapspac 一开始使用 findBugs 进行扫描 并未扫描出可用结果 首先 介绍一个免费开源分析 dump 的软件 MemoryAnalyz 下载地址如下所示 同事介绍 psaux

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

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

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

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

ps aux | grep xxx 查询进程ID

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

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

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

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

b43b73bc8960219c6bc47129023c9927.png

发现问题出现在了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://javaforall.net/210751.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午11:59
下一篇 2026年3月18日 下午11:59


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号