321

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

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

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

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

第一种方法效果很好,第二种方法在另一个 java.lang.OutOfMemoryError 中结束,这次是关于堆。

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

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

4

16 回答 16

157

您基本上耗尽了内存来顺利运行该过程。想到的选项:

  1. -Xmx512m像你提到的那样指定更多的内存,先试试中间的东西
  2. HashMap如果可能,一次处理小批量的对象
  3. 如果您有很多重复的字符串,请先使用String.intern()它们,然后再将它们放入HashMap
  4. 使用HashMap(int initialCapacity, float loadFactor)构造函数来调整您的情况
于 2011-04-30T04:00:30.513 回答
61

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

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"
    }
}
于 2015-11-22T11:47:01.697 回答
43

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

java -XX:+UseConcMarkSweepGC

这意味着,默认情况下此选项不活动。因此,当您说您使用了选项“ +XX:UseConcMarkSweepGC”时,我假设您使用的是以下语法:

java -XX:+UseConcMarkSweepGC

这意味着您明确激活了此选项。对于Java HotSpot VM Options@本 文档的正确语法和默认设置

于 2012-02-03T09:16:10.450 回答
24

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

-XX:-UseConcMarkSweepGC

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

于 2011-10-12T14:38:50.043 回答
11

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

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

  2. 创建一个外观,允许您根据需要将这些 HashMap 分页进出内存。一个简单的 LRU 缓存可能只是票。

  3. 增加 JVM 可用的内存。如有必要,如果您拥有托管此野兽的机器的管理权,那么即使购买更多 RAM 也可能是最快、最便宜的解决方案。话虽如此:我通常不喜欢“投入更多硬件”解决方案,特别是如果可以在合理的时间范围内想到替代算法解决方案的话。如果你不断地在每一个问题上投入更多的硬件,你很快就会遇到收益递减规律。

你到底想做什么?我怀疑有更好的方法来解决您的实际问题。

于 2011-04-30T04:01:42.107 回答
10

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

于 2013-01-17T17:01:54.417 回答
9

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

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

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

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

于 2013-01-28T19:07:28.780 回答
8

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

于 2015-03-25T04:09:36.253 回答
5

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

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

于 2011-04-30T04:04:10.807 回答
5

如果您有java8,并且可以使用G1 Garbage Collector,则使用以下命令运行您的应用程序:

 -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-字符串重复数据删除/

于 2016-08-12T14:04:15.563 回答
3

借助 Eclipse MATVisualVM等配置文件工具修复应用程序中的内存泄漏

使用JDK 1.7.x或更高版本, use G1GC与其他 GC 算法中的 2% 不同,它在垃圾收集上花费 10%。

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

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

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

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

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

生产环境中的 Java G1 垃圾收集

积极的垃圾收集器策略

于 2015-12-15T18:22:46.437 回答
3

为此,请在 android 闭包下的应用程序 gradle 文件中使用以下代码。

dexOptions { javaMaxHeapSize "4g" }

于 2017-02-24T09:41:00.237 回答
2

如果出现错误:

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

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

于 2013-01-23T07:56:34.107 回答
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
于 2016-04-14T10:41:58.943 回答
1

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

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

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

于 2015-12-15T17:59:17.197 回答
0

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

于 2012-12-07T16:13:07.313 回答