在项目开发上线的过程中,最近发现一个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编程中,"java heap space"内存溢出是一个常见的问题,它通常发生在应用程序尝试分配超过JVM堆内存限制的对象时。这个问题对于任何Java开发者来说都至关重要,因为如果不妥善处理,可能会导致程序崩溃。以下是...
Java程序在运行过程中可能会遇到各种异常,其中"nested exception is java.lang.OutOfMemoryError: Java heap space"是一个常见的问题,通常发生在程序试图分配超过堆内存限制的空间时。这个错误表明Java虚拟机(JVM...
了解`java.lang.OutOfMemoryError: Java heap space`错误的原因和解决方案,有助于我们在编写Java程序时避免此类问题,确保程序的稳定运行。通过合理的内存管理和优化代码,我们可以有效地预防和解决这个问题。
文档《解决Java_heap_space问题.doc》、《JAVA内存溢出解决方案.doc》、《java解压rar_高效解压.docx》、《java_lang_OutOfMemoryError Java heap space错误及处理办法(收集整理、转) - jxzxm1_2的专栏 - 博客频道 -...
1. **分析错误信息**:错误信息通常会指出哪一部分内存出现问题,如`Java heap space`或`PermGen space`。 2. **日志分析**:查看应用程序或服务器日志,获取更详细的错误信息。 3. **代码审查**:检查代码中是否...
2. **Heap Space错误**:当堆内存不足时,会抛出`java.lang.OutOfMemoryError: Java heap space`异常。这是因为分配给JVM的堆内存太小,不足以支持当前应用的需求。可以通过增加`-Xmx`参数来设置最大堆内存大小。 #...
通常,CLASSPATH环境变量用于指定Java执行环境查找类文件的路径。然而,现代Java版本(如Java 8及更高版本)在没有配置CLASSPATH的情况下也能正常工作,因为它们会自动搜索当前目录(`.`表示当前目录,也就是`.;`)...
- **分析Heap Dump文件:** 使用工具如MAT (Memory Analyzer Tool) 的Dominator Tree视图来查找内存占用最高的对象。 - **代码层面分析:** 如果存在内存泄漏或其他内存使用不当的情况,可以使用BTrace等工具进行...
Jconsole监控Java应用 Jconsole是JDK自带的监控工具,可以对Java应用程序进行监控。...通过Jconsole工具,可以实时监控Java应用程序的性能和内存使用情况,帮助开发者和运维人员快速定位和解决问题。
当堆空间不足时,会触发Full GC(垃圾收集),如果仍然无法释放足够的空间,就会出现`OutOfMemoryError: Java heap space`错误。 2. **年轻代(Young Generation)**:包括Eden区和两个Survivor区(From、To)。...
如果Heap空间不足,就会抛出`java.lang.OutOfMemoryError: Java heap space`异常。 - **Method Area**:又称为非堆(Non-Heap),存储已加载的类信息、常量、静态变量等。如果Method Area空间耗尽,会出现`java....
`Java Heap Space`溢出是由于创建过多对象;`unable to create new native Thread`是因为JVM占用过多内存导致操作系统无法创建新线程。解决方法包括调整JVM参数,减少不必要的对象创建,以及优化代码结构。 2. **...
当Java虚拟机(JVM)无法为新的对象分配内存时,就会抛出`java.lang.OutOfMemoryError:Java heap space`异常。这通常发生在JVM的堆内存空间耗尽的情况下。本文将详细介绍导致该异常的原因以及具体的解决策略。 #### ...
java.lang.OutOfMemoryError: Java heap space ``` 初步判断可能的原因有: 1. **JVM内存设置不当**: - 项目的实际需求超出默认的JVM内存设置。 2. **内存泄露**: - 程序中存在未被正确回收的内存。 #### 五、...
2. **查找并安装WindowBuilder:** - 在Eclipse Marketplace界面顶部的搜索框中输入“SWTDesigner”并回车。 - 在搜索结果中选择第一个结果“WindowBuilder 1.9.1”,点击“Installed”进行安装。 - 安装完成后,...
内存溢出可能是由于PermGen Space程序中使用了大量jar或class,导致Java虚拟机装载类空间不够,或者是Java Heap Space Java虚拟机创建了太多的对象,或者是unable to create new native Thread JVM占用了太多内存...