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

时间:2011-04-30 03:49:33

标签: java hashmap heap heap-memory g1gc

我在一个程序中遇到这个错误,该程序创建了几个(几十万个)HashMap对象,每个对象有几个(15-20)个文本条目。在提交到数据库之前,必须收集这些字符串(不分成少量)。

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

显然,可以使用命令行将参数传递给JVM for

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

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

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

问题也在related topic in StackOverflow.

中讨论

16 个答案:

答案 0 :(得分:157)

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

  1. 像你提到的那样指定更多内存,尝试介于-Xmx512m首先
  2. 之间的内容
  3. 如果可能,使用较小批量的HashMap个对象进行处理
  4. 如果您有很多重复的字符串,请先将String.intern()放在HashMap
  5. 使用HashMap(int initialCapacity, float loadFactor)构造函数调整案例

答案 1 :(得分:60)

以下对我有用。只需添加以下代码段:

unmovable&

dexOptions { javaMaxHeapSize "4g" }

build.gradle

答案 2 :(得分:42)

@takrl:此选项的默认设置为:

java -XX:+UseConcMarkSweepGC

表示默认情况下此选项不活动。所以当你说你使用了这个选项 “+XX:UseConcMarkSweepGC” 我假设您使用的是这种语法:

java -XX:+UseConcMarkSweepGC

表示您明确激活了此选项。 有关Java HotSpot VM Options @ this的正确语法和默认设置 document

答案 3 :(得分:23)

为了记录,我们今天遇到了同样的问题。我们使用此选项修复了它:

-XX:-UseConcMarkSweepGC

显然,这修改了用于垃圾收集的策略,这使问题消失了。

答案 4 :(得分:12)

嗯......你要么:

  1. 完全重新考虑您的算法&数据结构,因此它不需要所有这些小HashMaps。

  2. 创建一个外观,允许您根据需要在内存中分页HashMaps。一个简单的LRU缓存可能就是故障单。

  3. 向上提供JVM可用的内存。如果你有管理这台野兽的机器,即使购买更多的RAM也可能是最快,最便宜的解决方案。话虽如此:我通常不是“投入更多硬件”解决方案的粉丝,特别是如果可以在合理的时间范围内考虑替代算法解决方案。如果你不断在每一个问题上投入更多的硬件,你很快就会遇到收益递减规律。

  4. 你到底想要做什么?我怀疑你的实际问题有更好的方法。

答案 5 :(得分:9)

使用备用HashMap实现(Trove)。标准Java HashMap具有> 12倍的内存开销。 人们可以阅读详细信息here

答案 6 :(得分:9)

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

将中间结果写入数据库中的临时表而不是hashmaps - 从功能上讲,数据库表相当于hashmap,即两者都支持键控访问数据,但表不受内存限制,因此使用索引表这里而不是哈希图。

如果操作正确,您的算法甚至不应该注意到更改 - 这里正确意味着使用类来表示表,甚至给它一个put(键,值)和get(key)方法就像一个hashmap。

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

答案 7 :(得分:8)

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

答案 8 :(得分:5)

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

您应该尝试重新考虑您的算法。在这种情况下,我会就该主题提供更多帮助,但在您提供有关问题背景的更多信息之前,我无法提供任何信息。

答案 9 :(得分:5)

如果您有 java8 ,并且可以使用 G1垃圾收集器,请运行您的应用程序:

 -XX:+UseG1GC -XX:+UseStringDeduplication

这告诉G1找到类似的字符串,只保留其中一个在内存中,其他只是指向内存中String的指针。

当你有很多重复的字符串时,这很有用。此解决方案可能或不起作用,取决于每个应用程序。

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

答案 10 :(得分:3)

借助eclipse MATVisualVM

等个人资料工具修复应用程序中的内存泄漏

使用JDK 1.7.x或更高版本,使用G1GC ,其垃圾收集费用为10%,而其他GC算法则为2%。 < / p>

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

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

查看oracle文章,了解这些参数的微调。

与SE中的G1GC有关的一些问题:

Java 7 (JDK 7) garbage collection and documentation on G1

Java G1 garbage collection in production

Agressive garbage collector strategy

答案 11 :(得分:2)

如果出现错误:

  

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

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

答案 12 :(得分:2)

您需要在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

答案 13 :(得分:2)

对于此用法,请在android闭包下的app gradle文件中使用代码。

dexOptions {     javaMaxHeapSize“4g” }

答案 14 :(得分:1)

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

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

还有另外一点。当我在用户帐户中设置JAVA_OPTSCATALINA_OPTS并再次增加内存量时,我得到了同样的错误。然后,我在我的代码中打印了那些环境变量的值,这给了我不同于我设置的值。原因是Tomcat是该进程的根,然后因为我不是su-doer,我要求管理员增加Tomcat中catalina.sh的内存。

答案 15 :(得分:0)

这有助于我摆脱这个错误。此选项禁用 -XX:+ DisableExplicitGC