关于hashmap:java.lang.outofmemoryError:gc超出开销限制 GC overhead limit exceeded

java.lang.OutOfMemoryError: GC overhead limit exceeded

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

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

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

  • 通过"-xmx1024m"(或更多)增加堆大小,或者
  • 通过"-xx:-usegcoverheadlimit"完全禁用错误检查。

第一种方法工作正常,第二种方法最后出现在另一个java.lang.OutOfMemoryError中,这次是关于堆的。

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

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


基本上,您的内存不足,无法顺利运行该进程。想到的选择:

  • 像前面提到的那样指定更多的内存,首先尝试在-Xmx512m之间进行一些操作。
  • 如果可能的话,与小批量的HashMap对象一起处理。
  • 如果有很多重复的字符串,在将它们放入HashMap之前,在它们上使用String.intern()
  • 使用HashMap(int initialCapacity, float loadFactor)构造函数为您的案例进行优化

  • 以下内容对我很有用。只需添加以下代码段:

    1
    2
    3
    dexOptions {
            javaMaxHeapSize"4g"
    }

    致您的build.gradle

    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

    这意味着你明确地激活了这个选项。为了正确的语法和Java HotSpot VM Options的默认设置,@this文件


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

    1
    -XX:-UseConcMarkSweepGC

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


    嗯…您要么需要:

  • 完全重新考虑您的算法和数据结构,这样它就不需要所有这些小哈希图。

  • 创建一个外观,允许您根据需要在内存中或内存外分页这些哈希图。一个简单的LRU缓存可能只是一张罚单。

  • 为JVM增加可用内存。如果有必要,甚至购买更多的RAM可能是最快、最便宜的解决方案,如果您拥有托管这种野兽的机器的管理。尽管如此:我通常不喜欢"在IT上投入更多硬件"的解决方案,特别是如果在合理的时间内可以考虑到其他算法解决方案。如果你不断地在这些问题上投入更多的硬件,你很快就会遇到收益递减的规律。

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


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

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

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

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


    使用替代的hashmap实现(trove)。标准Java HASMAP具有12X内存开销。你可以在这里看到细节。


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


    如果你正在创建成百上千的散列图,你可能使用的远远超过你实际需要的;除非你使用大文件或图形,存储简单的数据不应该溢出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等配置文件工具修复应用程序中的内存泄漏

    对于JDK 1.7.x或更高版本,使用G1GC,它在垃圾收集上花费10%,而在其他GC算法中则为2%。

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

    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


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

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

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


    这有助于我消除此错误。此选项禁用-xx:+禁用explicitgc