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


804

我执行JUnit测试时收到以下错误消息:

java.lang.OutOfMemoryError: GC overhead limit exceeded

我知道是什么OutOfMemoryError,但是GC开销限制是什么意思?我该如何解决?


16
听起来很有趣。我希望有人可以发布一些生成此代码的代码。
2009年

1
我只是发现了一个问题,即导致过多的内存使用,接近堆的极限。一个简单的解决方案可能只是为Java引擎(-Xmx)提供更多的堆内存,但这仅在应用程序需要的内存与之前设置的堆限制一样多时才有帮助。
Mnementh,2009年

1
@Mnementh我在这里给出了答案,请检查它是否对stackoverflow.com/questions/11091516/…
lulu

16
@SimonKuang请注意,在多种OutOfMemoryError情况下,增加堆不是有效的解决方案:用尽本机线程和用尽perm gen(与堆分开)是两个示例。对过于宽泛的陈述要谨慎OutOfMemoryErrors; 有一系列意想不到的事情可以导致它们。
蒂姆(Tim)

3
您是如何解决这个问题的?
Thorsten Niehues

Answers:


763

此消息表示由于某种原因,垃圾收集器占用了过多的时间(默认情况下为进程所有CPU时间的98%),并且每次运行时恢复的内存很少(默认为堆的2%)。

这实际上意味着您的程序停止任何进展,并且一直在忙于仅运行垃圾回收。

为了防止您的应用程序浪费CPU时间而不做任何事情,JVM抛出此错误,Error以便您有机会诊断问题。

我见过这种情况的罕见情况是,某些代码在已经非常受内存限制的环境中创建了大量的临时对象和大量的弱引用对象。

查阅Java GC调整指南,该指南可用于各种Java版本,并包含有关此特定问题的部分:


9
将您的答案总结如下是正确的:“这就像是'Java堆空间不足'错误。使用-Xmx为它提供更多内存。” ?
蒂姆·库珀

58
@Tim:不,那是不正确的。虽然为它提供更多的内存可以减少问题,但您还应该查看代码,看看为什么它会产生那么多的垃圾,以及为什么代码在“内存不足”标记的下方略读。这通常是代码损坏的迹象。
Joachim Sauer 2010年

8
谢谢,看来Oracle在数据迁移方面实际上并不那么出色,他们打破了联系。
约阿希姆·绍尔

151
您在“谢谢,看来甲骨文实际上并不是那么好”上让我过
Rob Grant

3
@Guus:如果多个应用程序在同一个JVM中运行,那么可以,它们很容易相互影响。很难说出哪个是行为不端。将应用程序分离到不同的JVM中可能是最简单的解决方案。
约阿希姆·绍尔

215

引用Oracle的文章“ Java SE 6 HotSpot [tm]虚拟机垃圾收集优化”

过多的GC时间和OutOfMemoryError

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

编辑:看起来有人可以比我更快地输入:)


87
“您可以关闭此功能...”,但是OP很可能不应该这样做。
Stephen C

2
您能告诉我“ -XX”和“ -Xmx”之间的区别吗?我也可以使用“ -Xmx”选项将其关闭。
Susheel Javadi

19
@Bart -XX:在几个命令行选项的开头是一个标志,表明此选项是高度特定于VM的并且不稳定(在将来的版本中可能会有所更改,恕不另行通知)。无论如何,该-XX:-UseGCOverheadLimit标志告诉VM禁用GC开销限制检查(实际上是“关闭”),而您的-Xmx命令只是增加了堆空间。在后一种情况下,GC开销检查仍在运行,这听起来像是更大的堆解决了您的情况下的GC崩溃问题(这并不总是有帮助)。
Andrzej Doyle

1
在我的应用程序中(在Talend中读取大的Excel文件),此方法不起作用,并且从其他用户的解释中,我理解了原因。这只是禁用了错误,但问题仍然存在,您的应用程序将仅花费大部分时间来处理GC。我们的服务器有足够的RAM,因此我使用了Vitalii的建议来增加堆大小。
RobbZ 2016年

如果您的应用程序是数据密集型应用,则最终将出现此错误,清除内存并避免数据泄漏是最好的解决方法-但需要一些时间。
彼得维斯

89

如果您确定程序中没有内存泄漏,请尝试:

  1. 例如,增加堆大小 -Xmx1g
  2. 启用并发的低暂停收集器 -XX:+UseConcMarkSweepGC
  3. 尽可能重用现有对象以节省一些内存。

如有必要,可以通过在命令行中添加选项来禁用限制检查-XX:-UseGCOverheadLimit


9
我不同意第三点建议。重用现有对象不会节省内存(不要泄漏旧对象可以节省内存:-)此外,“重用现有对象”是减轻GC压力的一种做法。但这并非总是一个好主意:使用现代GC,我们应该避免旧对象容纳新对象的情况,因为它可能会破坏某些局部性假设……
mcoolive

@mcoolive:有关某个人为的示例,请参见下面的注释以回答stackoverflow.com/a/5640498/4178262List在循环内部创建对象导致GC被调用39次而不是22次。
马克·斯图尔特

45

通常是代码。这是一个简单的例子:

import java.util.*;

public class GarbageCollector {

    public static void main(String... args) {

        System.out.printf("Testing...%n");
        List<Double> list = new ArrayList<Double>();
        for (int outer = 0; outer < 10000; outer++) {

            // list = new ArrayList<Double>(10000); // BAD
            // list = new ArrayList<Double>(); // WORSE
            list.clear(); // BETTER

            for (int inner = 0; inner < 10000; inner++) {
                list.add(Math.random());
            }

            if (outer % 1000 == 0) {
                System.out.printf("Outer loop at %d%n", outer);
            }

        }
        System.out.printf("Done.%n");
    }
}

在Windows 7 32位上使用Java 1.6.0_24-b07。

java -Xloggc:gc.log GarbageCollector

然后看 gc.log

  • 使用BAD方法触发444次
  • 使用WORSE方法触发666次
  • 使用BETTER方法触发354次

现在,这不是最佳的测试或最佳的设计,但是当您除了别无选择,只能实现这样的循环或者处理行为不佳的现有代码时,选择重用对象而不是创建新对象可以减少垃圾收集器阻塞的次数...


12
请说明:当您说“触发n次”时,是否意味着常规GC发生了n次,或者OP报告的“超出GC开销限制”错误发生了n次?
乔恩·施耐德

我刚刚使用Java 1.8.0_91进行了测试,但从未出现错误/异常,“触发n次”是从计算gc.log文件中的行数开始的。我的测试总体上显示的次数要少得多,但是对BETTER来说,“触发”次数最少,现在,BAD比现在的WORST更“糟糕”。我的罪状:BAD:26,更糟糕:22,BETTER 21
马克·斯图尔特

我刚刚添加了一个“ WORST_YET”修改,在其中我List<Double> list外部循环中而不是外部循环之前定义了,并触发了39个垃圾回收。
马克·斯图尔特

36

根据Java [8] Platform,Standard Edition故障排除指南,错误的原因:(添加了强调和换行符)

超过“ GC开销限制”表示垃圾收集器一直在运行,并且Java程序的进度非常缓慢。

进行垃圾回收之后,如果Java进程花费了其大约98%的时间用于垃圾回收,并且正在恢复的内存少于2%,并且到目前为止已经执行了最后5个(编译时间常数)连续垃圾集合,然后java.lang.OutOfMemoryError抛出a。[...]

  1. 增加堆大小如果当前堆不够,请。
  2. 如果增加堆内存后仍然出现此错误,请使用内存分析工具,例如MAT(内存分析器工具), Visual VM等,并修复内存泄漏。
  3. 将JDK版本升级到最新版本(1.8.x)或至少1.7.x,并使用G1GC算法。。G1 GC的吞吐量目标是90%的应用时间和10%的垃圾收集时间
  4. 除了使用-设置堆内存之外Xms1g -Xmx2g,请尝试

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

看看有关G1GC的一些其他相关问题


29

只需在以下位置设置此选项即可稍微增加堆大小

运行→运行配置→参数→VM参数

-Xms1024M -Xmx2048M

Xms-最小限制

Xmx-最大限制


2
android应用程序没有arguments选项卡...我们应该怎么做才能做到这一点?
Blaze Tama 2014年

3
答案是什么?那不是Eclipse的问题。
Michael Piefel

3
没有“最低限额”。-Xms是初始大小。
Diego Queiroz

1
可以设置的最大限制的最大值是多少?
JPerk

14

对我来说,以下步骤有效:

  1. 开启eclipse.ini档案
  2. 更改

    -Xms40m
    -Xmx512m

    -Xms512m
    -Xmx1024m
  3. 重新启动Eclipse

看这里


解决此问题的最简单方法。谢谢:)
Hamza 2015年

1
jdev中的eclipse.ini文件?
Abhinaba Basu 2015年

即使将配置更改为此也无法解决问题。
zionpi '16

1
OP没有询问Eclipse问题。
Michael Piefel

1
此“答案”没有回答上面的问题。
Freitags '17

13

尝试这个

打开build.gradle文件

  android {
        dexOptions {
           javaMaxHeapSize = "4g"
        }
   }

非常适合模拟器。知道这如何影响实际设备吗?即这是一个好主意还是只是掩盖了问题?谢谢。
约书亚·品特

11

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

android {
        compileSdkVersion 25
        buildToolsVersion '25.0.1'

defaultConfig {
        applicationId "yourpackage"
        minSdkVersion 10
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
    }
dexOptions {
        javaMaxHeapSize "4g"
    }
}

是的,使用Gradle时:)
Alex

4
您甚至怎么可能认为这是对他的问题的总体解决方案?您将堆大小设置为4g,对于Android facepalm来说,这在gradle配置中是完全任意的。
朱利安·

7

在build.gradle(Module:app)文件中增加javaMaxHeapsize

dexOptions {
    javaMaxHeapSize "1g"
}

到(在gradle中添加此行)

 dexOptions {
        javaMaxHeapSize "4g"
    }

3

Java堆大小说明(xms,xmx,xmn)

-Xms size in bytes

Example : java -Xms32m

设置Java堆的初始大小。默认大小为2097152(2MB)。该值必须是1024字节(1KB)的倍数,并且大于1024字节(1KB)。(-server标志将默认大小增加到32M。)

-Xmn size in bytes

Example : java -Xmx2m

设置Eden代的初始Java堆大小。默认值为640K。(-server标志将默认大小增加到2M。)

-Xmx size in bytes

Example : java -Xmx2048m

设置Java堆可以增长的最大大小。默认大小为64M。(-server标志将默认大小增加到128M。)最大堆限制为大约2 GB(2048MB)。

Java内存参数(xms,xmx,xmn)格式

设置Java堆大小时,应使用字母“ m”或“ M”(对于MB)或“ g”或“ G”(对于GB)之一来指定内存参数。如果您指定“ MB”或“ GB”,则您的设置将无效。有效参数如下所示:

-Xms64m或-Xms64M -Xmx1g或-Xmx1G也可以使用2048MB指定2GB。此外,请确保在指定参数时仅使用整数。使用-Xmx512m是有效选项,但-Xmx0.5g会导致错误。

此参考资料可能对某人有所帮助。


2

您还可以通过将以下内容添加到gradle.properties文件中来增加内存分配和堆大小:

org.gradle.jvmargs=-Xmx2048M -XX\:MaxHeapSize\=32g

它不必是2048M和32g,可以随心所欲地变大。


2

解决:
只需添加
org.gradle.jvmargs=-Xmx1024m

gradle.properties
如果不存在,则创建它。


0

我在Android Studio中工作,尝试生成用于发布的签名APK时遇到此错误。我能够毫无问题地构建和测试调试APK,但是一旦我要构建发行版APK,构建过程将最终运行数分钟,然后最终终止,并显示“错误java.lang.OutOfMemoryError:GC超出了开销限制”。我增加了VM和Android DEX编译器的堆大小,但是问题仍然存在。最终,经过数小时和大量的咖啡后,事实证明问题出在我的应用程序级别的“ build.gradle”文件中-我将发布版本类型的“ minifyEnabled”参数设置为“ false”,因此运行了Proguard有关尚未通过代码缩减的过程的代码(请参阅https://developer.android。)。我将'minifyEnabled'参数更改为'true',并像梦一样执行了发布版本:)

简而言之,我必须将应用程序级别的“ build.gradle”文件从以下位置更改:// ...

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.sign_config_release
    }
    debug {
        debuggable true
        signingConfig signingConfigs.sign_config_debug
    }
}

//...

    //...

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.sign_config_release
    }
    debug {
        debuggable true
        signingConfig signingConfigs.sign_config_debug
    }
}

//...

0

要在IntelliJ IDEA中增加堆大小,请遵循以下说明。它为我工作。

对于Windows用户,

转到安装IDE的位置,然后搜索以下内容。

idea64.exe.vmoptions

编辑文件并添加以下内容。

-Xms512m
-Xmx2024m
-XX:MaxPermSize=700m
-XX:ReservedCodeCacheSize=480m

这就对了 !!


0

您可以尝试通过参考此图像在服务器设置上进行更改,并增加以黄色突出显示的处理过程更改的内存大小

您还可以通过打开cmd->来更改Java堆 set _java_opts -Xmx2g
2g(2gigabytes)具体取决于程序的复杂性

尝试使用较少的常量和临时变量

在此处输入图片说明


-1

您需要在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**
)

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**

4
这些设置仅特定于您的本地IDE。这在Prod环境中不起作用。
冯氏

-1

在Netbeans中,设计最大堆大小可能会有所帮助。转到运行 => 设置项目配置 => 定制。在其弹出的运行窗口中,转到VM Option,然后填写-Xms2048m -Xmx2048m。它可以解决堆大小问题。



By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.