java.lang.OutOfMemoryError:超出GC开销限制

我得到这个错误的程序创build几个(数十万)HashMap对象与几个(15-20)文本项每个。 在提交到数据库之前,这些string都被收集起来(没有分成更小的数量)。

据Sun介绍,错误发生在“如果在垃圾收集中花费了太多时间:如果超过总时间的98%用于垃圾收集,并且小于2%的堆被恢复,则将抛出OutOfMemoryError。 ”。

显然,可以使用命令行将parameter passing给JVM

  • 增加堆大小,通过“-Xmx1024m”(或更多)或
  • 完全禁用错误检查,通过“-XX:-UseGCOverheadLimit”。

第一种方法工作正常,第二种结束在另一个java.lang.OutOfMemoryError,这次是关于堆。

所以,问题:是否有任何程序的替代scheme,对于特定的用例(即几个小的HashMap对象)? 例如,如果我使用HashMap clear()方法,问题就消失了,但是存储在HashMap中的数据也是如此! 🙂

在StackOverflow的相关主题中也讨论了这个问题。

你基本上没有足够的内存来顺利运行这个过程。 想到的选项:

  1. 像上面提到的那样指定更多的内存,先尝试一下像-Xmx512m类的东西
  2. 如果可能的话,使用较小批次的HashMap对象进行处理
  3. 如果你有很多重复的string,在把它们放入HashMap之前,先使用String.intern()
  4. 使用HashMap(int initialCapacity, float loadFactor)构造函数调整您的情况

以下为我工作。 只需添加下面的代码片段:

 dexOptions { javaMaxHeapSize "4g" } 

到你的build.gradle

 android { compileSdkVersion 23 buildToolsVersion '23.0.1' defaultConfig { applicationId "yourpackage" minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName "1.0" multiDexEnabled true } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } packagingOptions { } dexOptions { javaMaxHeapSize "4g" } } 

@takrl:这个选项的默认设置是:

 java -XX:+UseConcMarkSweepGC 

这意味着,这个选项默认是不活动的。 所以当你说你使用了“ +XX:UseConcMarkSweepGC ”选项时,我假设你使用的是这个语法:

 java -XX:+UseConcMarkSweepGC 

这意味着你明确地激活了这个选项。 有关Java HotSpot VM Options @@本文档的正确语法和默认设置

为了logging,我们今天有同样的问题。 我们使用这个选项来修复它:

 -XX:-UseConcMarkSweepGC 

显然,这个修改了垃圾回收的策略,这个问题就消失了。

呃…你要么需要:

  1. 完全重新思考你的algorithm和数据结构,这样它不需要所有这些小的HashMap。

  2. 创build一个门面,允许您根据需要对这些HashMaps进行内存访问。 一个简单的LRUcaching可能就是票。

  3. 提供可用于JVM的内存。 如果有必要的话,即使购买更多的内存可能是最快的,最便宜的解决scheme,如果你有托pipe这个野兽的机器的pipe理。 话虽如此:我一般不是“投入更多硬件”的解决scheme,特别是如果在合理的时间范围内可以考虑替代的algorithm解决scheme。 如果你在这些问题的每一个问题上不断投入更多的硬件,你很快就会遇到收益递减的规律。

你究竟在做什么呢? 我怀疑有一个更好的方法来解决你的实际问题。

使用替代的HashMap实现( Trove )。 标准的Java HashMap具有> 12倍的内存开销。 可以在这里阅读细节。

在等待结束时,不要将整个结构存储在内存中。

将中间结果写入数据库中的临时表而不是hashmaps–在function上,数据库表等价于一个hashmap,即既支持对数据的键控访问,也不支持内存绑定,所以在这里使用索引表而不是hashmaps。

如果做得正确,你的algorithm甚至不应该注意到变化 – 正确的意思是使用一个类来表示表,甚至像hashmap一样给它一个put(key,value)和一个get(key)方法。

当中间表完成时,从内存中而不是从内存中生成所需的sql语句。

如果在垃圾收集中花费了太多时间,并行收集器将会抛出一个OutOfMemoryError 。 特别是,如果超过98%的时间用于垃圾回收,并且不足2%的堆被恢复,则会抛出OutOfMemoryError 。 此function旨在防止应用程序长时间运行,而由于堆太小,进行很less或没有进度。 如有必要,可以通过在命令行中添加-XX:-UseGCOverheadLimit选项来禁用此function。

如果你正在创build数十万个哈希映射,你可能使用的远远超过你实际需要的; 除非您使用大型文件或graphics,否则存储简单数据不应该溢出Java内存限制。

你应该尝试重新思考你的algorithm。 在这种情况下,我会提供更多关于这个问题的帮助,但是在你提供更多关于问题的背景之前,我不能提供任何信息。

如果你有java8 ,你可以使用G1垃圾收集器 ,那么运行你的应用程序:

  -XX:+UseG1GC -XX:+UseStringDeduplication 

这告诉G1find类似的string,并只保留其中的一个在内存中,其他人只是一个指向该string的内存。

当你有很多重复的string时,这很有用。 该解决scheme可能或不可行,并取决于每个应用程序。

更多信息:
https://blog.codecentric.de/en/2014/08/string-deduplication-new-feature-java-8-update-20-2/ http://java-performance.info/java-string-deduplication/

在eclipse MAT或VisualVM等configuration文件工具的帮助下,修复内存泄漏

使用JDK 1.7.x或更高版本时,使用G1GC它在垃圾收集上花费10%,而不像其他GCalgorithm中的2%。

除了使用-Xms1g -Xmx2g设置堆内存-Xms1g -Xmx2g ,请尝试使用`

 -XX:+UseG1GC -XX:G1HeapRegionSize=n, -XX:MaxGCPauseMillis=m, -XX:ParallelGCThreads=n, -XX:ConcGCThreads=n` 

看看oracle文章,以微调这些参数。

SE中有关G1GC的一些问题:

Java 7(JDK 7)垃圾收集和文档上的G1

生产中的Java G1垃圾收集

积极的垃圾收集器战略

在出现错误的情况下:

“内部编译器错误:java.lang.OutOfMemoryError:超出了java.lang.AbstractStringBuilder的GC开销限制”

将java堆空间增加到2GB即-Xmx2g.

您需要增加Jdeveloper中的内存大小,然后转到setDomainEnv.cmd。

 set WLS_HOME=%WL_HOME%\server set XMS_SUN_64BIT=256 set XMS_SUN_32BIT=256 set XMX_SUN_64BIT=3072 set XMX_SUN_32BIT=3072 set XMS_JROCKIT_64BIT=256 set XMS_JROCKIT_32BIT=256 set XMX_JROCKIT_64BIT=1024 set XMX_JROCKIT_32BIT=1024 if "%JAVA_VENDOR%"=="Sun" ( set WLS_MEM_ARGS_64BIT=-Xms256m -Xmx512m set WLS_MEM_ARGS_32BIT=-Xms256m -Xmx512m ) else ( set WLS_MEM_ARGS_64BIT=-Xms512m -Xmx512m set WLS_MEM_ARGS_32BIT=-Xms512m -Xmx512m ) and set MEM_PERM_SIZE_64BIT=-XX:PermSize=256m set MEM_PERM_SIZE_32BIT=-XX:PermSize=256m if "%JAVA_USE_64BIT%"=="true" ( set MEM_PERM_SIZE=%MEM_PERM_SIZE_64BIT% ) else ( set MEM_PERM_SIZE=%MEM_PERM_SIZE_32BIT% ) set MEM_MAX_PERM_SIZE_64BIT=-XX:MaxPermSize=1024m set MEM_MAX_PERM_SIZE_32BIT=-XX:MaxPermSize=1024m 

对于这个使用下面的代码在你的应用程序gradle文件下的androidclosures。

dexOptions {javaMaxHeapSize“4g”}

对于我的情况增加使用-Xmx选项的内存是解决scheme。

我有一个10g文件在Java中读取,每次我得到相同的错误。 当top命令中的RES列中的值达到-Xmx选项中设置的值时,会发生这种情况。 然后通过增加内存使用-Xmx选项一切都很好。

还有一点。 当我在我的用户帐户中设置JAVA_OPTSCATALINA_OPTS并再次增加内存量时,我得到了同样的错误。 然后,我在代码中输出了这些环境variables的值,这些variables赋予了我设置的不同的值。 原因是Tomcat是这个过程的根源,然后我不是一个老板,我要求pipe理员增加Tomcat中catalina.sh的内存。

这帮助我摆脱了这个错误。这个选项禁用-XX:+ DisableExplicitGC