2011-04-30 129 views
306

我在一个程序中创建了几个(数十万个)HashMap对象,每个对象都有几个(15-20)个文本项。这些字符串在提交到数据库之前都已收集(没有分解成更小的数量)。java.lang.OutOfMemoryError:超出GC开销限制

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

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

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

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

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

这个问题也是在related topic in StackOverflow.

+1

您可能需要改变你的算法,并使用一些更有效的数据结构。你能告诉我们你正在试图实现哪种算法,这需要大量的HashMaps吗? – Ankur 2011-04-30 03:59:47

+0

我只是阅读非常大的文本文件(每个数十万行),我无法控制它们,即它们不能被分解。对于每一行文本,都会构造一个HashMap,其中包含少数(实际上大约10个)小字符串值,并且一次又一次使用相同的数据库字段名称。理想情况下,我希望在将数据发送到数据库之前能够读取整个文件。 – PNS 2011-04-30 09:32:34

+1

听起来好像在将数据发送到数据库之前读取整个文件实际上是一个很糟糕的解决方案......实际上它根本无法工作,在可用内存的非常实际的限制之内。无论如何,你为什么要这么做? “一次又一次使用相同的数据库字段名”是什么意思?字段名称作为键或值?如果他们的字段是键,那么只需使用一个数组,其中的字段被它的位置所隐含......如果它们是值,那么在将它们添加到地图之前将它们实习。这有助于了解数据是什么。干杯。基思。 – corlettk 2011-04-30 10:22:39

回答

154

你基本上运行内存流畅运行过程中进行讨论。

  1. 指定更多的内存就像你提到的,尝试一些在-Xmx512m之间像第一
  2. 工作与HashMap对象的小批量如果你有很多东西需要一次如果可能的话
  3. 处理:浮现在脑海中的选项重复字符串,用他们String.intern()将它们放入了HashMap
  4. 使用HashMap(int initialCapacity, float loadFactor)构造函数来调整你的情况之前
+1

我已经使用了接近HashMap的初始容量,所以程序在那里几乎是最优的。 – PNS 2011-04-30 09:56:43

+2

如果它的内存更多,是否有什么理由不去?如果你使用诸如'-Xms128m -Xmx1024m'之类的东西,它实际上只会增长到最大。似乎最简单的选择。 – WhiteFang34 2011-04-30 10:03:40

+1

是的,我猜最快。我用intern()来表达一些可能重复的值,并且问题也消失了。 – PNS 2011-04-30 11:11:47

11

嗯......你要么需要:

  1. 重新考虑你的算法&数据结构,使得它并不需要所有这些小HashMaps这样。

  2. 创建一个门面,允许您根据需要对这些HashMaps进行内存进出。简单的LRU缓存可能就是票证。

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

你究竟想要做什么?我怀疑你的实际问题有更好的方法。

+0

看到我上面的评论。这个用例非常简单,我正在寻找一种方法来处理整个大文件而不中断流程。谢谢! – PNS 2011-04-30 09:52:47

5

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

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

+0

看到我上面的评论。这个用例非常简单,我正在寻找一种方法来处理整个大文件而不中断流程。谢谢! – PNS 2011-04-30 09:55:28

23

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

-XX:-UseConcMarkSweepGC 

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

40

@takrl:该选项的默认设置是:

java -XX:+UseConcMarkSweepGC 

这意味着,这个选项是默认情况下不活跃。所以,当你说你使用的选项 “+XX:UseConcMarkSweepGC” 我假设你正在使用的语法如下:

java -XX:+UseConcMarkSweepGC 

这意味着你明确地激活这个选项。 对于@这个 document

+0

在我们使用-XX:+ UseConcMarkSweepGC的情况下,在高负载/高内存压力的情况下,“OutOfMemoryError:GC开销限制超出”错误的风险降低了一点,但另一方面它使用了更多的CPU,在正常负载情况下,执行时间延长5-10%。 – anre 2016-10-13 20:40:19

0

帮我摆脱这种的Java HotSpot VM Options正确的语法和默认设置error.This选项禁用 -XX:+ DisableExplicitGC

9

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

+6

\t net.sf.trove4j \t trove4j \t 3.0.3 Jeef 2014-05-16 21:43:02

2

在错误的情况下:

"Internal compiler error: java.lang.OutOfMemoryError: GC overhead limit exceeded at java.lang.AbstractStringBuilder"

增加Java堆空间为2GB,即-Xmx2g.

9

整个结构不存储在内存中,同时等待到达终点。

将中间结果写入数据库中的临时表而不是hashmaps--在功能上,数据库表等同于一个散列表,即既支持对数据的键控访问,也不支持内存绑定,因此使用索引这里的表格而不是hashmaps。

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

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

8

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

+0

你从哪里得到这个信息?我很感兴趣,因为它看起来非常非常正确。发现它...... ---> http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html#par_gc.oom – MaasSql 2015-04-17 12:41:04

59

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

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" 
    } 
} 
+0

更多详情请参阅: http:// google .github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.DexOptions.html – 2015-12-16 07:53:20

+0

我做到了这一点,并且我仍然没有足够的空间。 Android Studio 2.2.3。 – 2017-02-14 01:45:21

+0

你的gradle版本是什么? – 2017-02-14 13:33:52

1

对于我来说,增加使用-Xmx选项内存是解决办法。

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

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

3

修复内存泄漏与轮廓工具帮助像Eclipse MATVisualVM

随着JDK 1.7.x或更高版本的应用程序,使用G1GC其垃圾回收花费10%不同于其他GC算法2%。

除了设置堆内存与-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

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 
5

如果你有java8,并且可以使用G1垃圾收集,然后运行你的应用程序:

-XX:+UseG1GC -XX:+UseStringDeduplication 

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

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

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

+0

谢谢乔治。帮助我编译Apache Camel: 导出MAVEN_OPTS =“ - Xms3000m -Xmx3000m -XX:+ UseG1GC -XX:+ UseStringDeduplication” – 2016-09-01 11:23:18

+0

欢迎大家关注CPU使用情况,因为G1 GC对它的要求更高一些。 – 2016-09-01 13:16:29

2

对于下面这段代码在使用你的应用程序文件的gradle关闭的android下。

dexOptions { javaMaxHeapSize “4G” }

相关问题