在项目开发上线的过程中,最近发现一个Dubbo服务隔7天左右就会出现以下问题:
Exception in thread "Timer-0" java.lang.OutOfMemoryError: Java heap space
(一开始使用findBugs进行扫描,并未扫描出可用结果)
首先,介绍一个免费开源分析dump的软件Memory Analyzer,下载地址如下所示:(同事介绍)
http://www.eclipse.org/mat/downloads.php
此工具需要依赖于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使用代码如下所示:
Cipher ci = Cipher.getInstance("RSA", new BouncyCastleProvider());
发现是坑张的代码,在服务每次使用的时候都会重新创建一个BouncyCastleProvider用来进行初始化密钥的工具类。
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方法代码进行了分析,代码如下所示:
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定义成单例就可以了。
相关推荐
java.lang.OutOfMemoryError: Java heap space 解决方法
解决Java_heap_space问题
搜集整理关于java错误处理:java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: Java heap space 资料整理
解决java抛java heap space
Myeclipse下java.lang.OutOfMemoryError Java heap space的解决
kettle内存溢出(Java heap space)以及解决方法
java heap space解决方法,网上搜的一个都没有解决,这是同事教我的。
Java heap space 解决方法,解决tomcat堆栈溢出问题.
java解决nested exception is java.lang.OutOfMemoryError Java heap space 解决OOM
NULL 博文链接:https://tlk20071.iteye.com/blog/1283378
NULL 博文链接:https://zhangzhi199129.iteye.com/blog/1659315
记录java.lang.OutOfMemoryError:Java heap space的情况 文章内容: 一.问题描述+原因分析+解决方案 二.JVM调优说明 三.Tomcat添加到jvisualvm监控
编译时出现java.lang.OutOfMemoryError Java heap space异常.
java虚拟机OutOfMemoryError:Java heap space堆dump文件,可以直接用来分析。
NULL 博文链接:https://geyubin.iteye.com/blog/779330
经常遇到Java heap space的问题,编译起来就没有效率可言了,对于一个开发者来说真的很痛苦。 之前网络上的方案各种改内存分配的数量,总的来说没什么真正的效果;现在来一个新的方案和大家分享;主要是替换掉一些三...
如果您的tomcat是6.X版本且是安装版的,那么系统设置环境变量是不起作用的,因为这个版本的只能通过点击tomcat6.exe 或tomcat6w.exe才能执行,这种方法可以通过修改注册表能解决:本例是以内存3G为例的。...
java[1].lang.OutOfMemoryError_Java_heap_space错误及处理办法java[1].lang.OutOfMemoryError_Java_heap_space错误及处理办法java[1].lang.OutOfMemoryError_Java_heap_space错误及处理办法
NULL 博文链接:https://xiaoxuejie.iteye.com/blog/1679119