java.lang.OutOfMemoryError: GC overhead limit exceeded
我在一个程序中得到这个错误,这个程序创建了几个(几十万)哈希映射对象,每个对象都有几个(15-20)文本条目。在提交到数据库之前,必须收集所有这些字符串(不拆分成较小的数量)。
根据Sun的说法,错误发生在"如果在垃圾收集中花费的时间太多:如果在垃圾收集中花费的总时间超过98%,并且回收的堆少于2%,则会引发OutOfMemoryError。"。
显然,可以使用命令行将参数传递给
- 通过"-xmx1024m"(或更多)增加堆大小,或者
- 通过"-xx:-usegcoverheadlimit"完全禁用错误检查。
第一种方法工作正常,第二种方法最后出现在另一个java.lang.OutOfMemoryError中,这次是关于堆的。
那么,问题是:对于特定的用例(例如,几个小的hashmap对象),是否有任何程序上的替代方法?例如,如果我使用hashmap clear()方法,问题就会消失,但是存储在hashmap中的数据也会消失!-)
在StackOverflow的相关主题中也讨论了这个问题。
基本上,您的内存不足,无法顺利运行该进程。想到的选择:
以下内容对我很有用。只需添加以下代码段:
1 2 3 | dexOptions { javaMaxHeapSize"4g" } |
致您的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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:此选项的默认设置为:
1 | java -XX:+UseConcMarkSweepGC |
这意味着,默认情况下该选项不处于活动状态。所以当你说你使用了这个选项"EDCOX1×5"我假设您使用的是这种语法:
1 | java -XX:+UseConcMarkSweepGC |
这意味着你明确地激活了这个选项。为了正确的语法和
据记录,我们今天也遇到了同样的问题。我们使用以下选项修复了它:
1 | -XX:-UseConcMarkSweepGC |
显然,这修改了用于垃圾收集的策略,使问题消失。
嗯…您要么需要:
完全重新考虑您的算法和数据结构,这样它就不需要所有这些小哈希图。
创建一个外观,允许您根据需要在内存中或内存外分页这些哈希图。一个简单的LRU缓存可能只是一张罚单。
为JVM增加可用内存。如果有必要,甚至购买更多的RAM可能是最快、最便宜的解决方案,如果您拥有托管这种野兽的机器的管理。尽管如此:我通常不喜欢"在IT上投入更多硬件"的解决方案,特别是如果在合理的时间内可以考虑到其他算法解决方案。如果你不断地在这些问题上投入更多的硬件,你很快就会遇到收益递减的规律。
你到底想做什么?我怀疑有更好的方法来解决你的实际问题。
不要在等待结束时将整个结构存储在内存中。
将中间结果写入数据库中的临时表而不是哈希映射-从功能上讲,数据库表相当于哈希映射,即两者都支持对数据的键控访问,但该表不受内存限制,因此请在此处使用索引表而不是哈希映射。
如果做得正确,您的算法甚至不应该注意到更改——这里正确的意思是使用一个类来表示表,甚至给它一个put(key,value)和get(key)方法,就像一个hashmap。
中间表完成后,从中生成所需的SQL语句,而不是从内存中生成。
使用替代的hashmap实现(trove)。标准Java HASMAP具有12X内存开销。你可以在这里看到细节。
如果在垃圾收集中花费了太多时间,并行收集器将抛出一个
如果你正在创建成百上千的散列图,你可能使用的远远超过你实际需要的;除非你使用大文件或图形,存储简单的数据不应该溢出Java内存限制。
您应该尝试重新考虑您的算法。在这种情况下,我会在这个主题上提供更多的帮助,但是在您提供更多关于问题背景的信息之前,我不能提供任何信息。
如果您有java8,并且可以使用g1垃圾收集器,那么使用以下命令运行应用程序:
1 | -XX:+UseG1GC -XX:+UseStringDeduplication |
这告诉g1找到类似的字符串,并只在内存中保留其中一个,而其他字符串只是指向内存中该字符串的指针。
当您有很多重复的字符串时,这很有用。此解决方案是否有效,取决于每个应用程序。
更多信息:https://blog.codecentric.de/en/2014/08/string-duplication-new-feature-java-8-update-20-2/http://java-performance.info/java-string-duplication/
借助EclipseMat或VisualVM等配置文件工具修复应用程序中的内存泄漏
对于
除了使用
1 2 3 4 5 | -XX:+UseG1GC -XX:G1HeapRegionSize=n, -XX:MaxGCPauseMillis=m, -XX:ParallelGCThreads=n, -XX:ConcGCThreads=n` |
请参阅Oracle文章,了解如何微调这些参数。
关于SE中g1gc的一些问题:
JAVA 7(JDK 7)垃圾收集和G1文档
JAVA G1垃圾回收在生产中的应用
积极的垃圾收集策略
为此,请在Android关闭下的应用程序渐变文件中使用以下代码。
右选项{javamaxheapsize"4G"}
您需要在jdeveloper中增加内存大小,请转到setdomainev.cmd。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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 |
如果出现错误:
"Internal compiler error: java.lang.OutOfMemoryError: GC overhead limit exceeded at java.lang.AbstractStringBuilder"
将Java堆空间增加到2GB,即EDCOX1×7
对于我来说,使用
我用Java读取了一个10G文件,每次都得到同样的错误。当
还有一点。当我在用户帐户中设置
这有助于我消除此错误。此选项禁用-xx:+禁用explicitgc