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

PNS*_*PNS 317 java heap heap-memory hashmap g1gc

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

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

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

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

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

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

StackOverflow中的相关主题也讨论了该问题.

Whi*_*g34 157

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

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

  • 如果它有更多的内存工作是否有任何理由不配合它?如果使用像-Xms128m -Xmx1024m这样的东西,它实际上只会增长到最大值.似乎是最简单的选择. (2认同)

Min*_*wzy 60

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

dexOptions {
        javaMaxHeapSize "4g"
}
Run Code Online (Sandbox Code Playgroud)

给你的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"
    }
}
Run Code Online (Sandbox Code Playgroud)


qup*_*era 42

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

java -XX:+UseConcMarkSweepGC
Run Code Online (Sandbox Code Playgroud)

这意味着,默认情况下此选项不活动.所以,当你说你使用选项" +XX:UseConcMarkSweepGC"时,我假设你使用的是这种语法:

java -XX:+UseConcMarkSweepGC
Run Code Online (Sandbox Code Playgroud)

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


tak*_*krl 23

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

-XX:-UseConcMarkSweepGC
Run Code Online (Sandbox Code Playgroud)

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


cor*_*ttk 12

嗯......你要么:

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

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

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

无论如何你还想做什么?我怀疑你的实际问题有更好的方法.


dir*_*dir 9

使用备用HashMap实现(Trove).标准Java HashMap具有> 12x的内存开销.人们可以在这里阅读细节.

  • <dependency> <groupId>net.sf.trove4j</groupId> <artifactId>trove4j</artifactId> <version>3.0.3</version> </dependency> (6认同)

Rod*_*ati 9

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

写中间结果到一个临时表在数据库中,而不是包含HashMap - 功能,数据库表是一个HashMap的相同,即都支持键控访问数据,但该表不是内存约束,所以这里使用索引表,而不是哈希图.

如果正确完成,你的算法甚至不应该注意到这种变化 - 这里正确的意思是使用一个类来表示表,甚至给它一个put(键,值)和一个get(key)方法就像一个hashmap.

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


小智 8

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


Rét*_*roX 5

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

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


Geo*_*e C 5

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

 -XX:+UseG1GC -XX:+UseStringDeduplication
Run Code Online (Sandbox Code Playgroud)

这告诉G1找到类似的字符串,并且只保留其中一个字符串,其他字符串只是内存中指向该字符串的指针.

当你有很多重复的字符串时,这很有用.该解决方案可能有效或无效,取决于每个应用程序.

更多信息:
https ://blog.codecentric.de/en/2014/08/string-deduplication-new-feature-java-8-update-20-2/ http://java-performance.info/java-串的重复数据删除/