首页 文章

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

提问于
浏览
311

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

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

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

  • 通过"-Xmx1024m"(或更多)增加堆大小,或

  • 通过"-XX:-UseGCOverheadLimit"完全禁用错误检查 .

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

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

该问题也在_775419中讨论

16 回答

  • 0

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

  • 156

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

  • 9

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

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

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

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

  • 9

    嗯......你要么:

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

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

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

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

  • 3

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

    • 指定更多内存,就像你提到的那样,首先尝试介于_775420之间的内容

    • 如果可能,使用较小批次的 HashMap 对象进行处理

    • 如果您有很多重复的字符串,请在将它们放入 HashMap 之前使用String.intern()

    • 使用HashMap(int initialCapacity, float loadFactor)构造函数调整您的案例

  • 23

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

    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"
        }
    }
    
  • 8

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

    java -XX:+UseConcMarkSweepGC
    

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

    java -XX:+UseConcMarkSweepGC
    

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

  • 2

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

    -XX:-UseConcMarkSweepGC
    

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

  • 11

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

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

  • 2

    如果你有 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-string-deduplication/

  • 59

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

    对于 JDK 1.7.x 或更高版本,请使用 G1GCwhich spends 10% on garbage collection unlike 2% in other GC algorithms.

    除了使用 -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

  • 5

    如果出现错误:

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

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

  • 41

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

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

    dexOptions {javaMaxHeapSize“4g”}

  • 1

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

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

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

  • 5

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

相关问题