Hata java.lang.OutOfMemoryError: GC genel masraf sınırı aşıldı


804

JUnit testlerimi yürütürken bu hata iletisini alıyorum:

java.lang.OutOfMemoryError: GC overhead limit exceeded

An'ın ne olduğunu biliyorum OutOfMemoryError, ama GC genel yük limiti ne anlama geliyor? Bunu Nasıl Çözebilirim?


16
Kulağa çok ilginç geliyor. Birisi bunu üreten bazı kod gönderebilir eğer isterim.
Buhb

1
Ben sadece yığın sınırına yakın, çok fazla bellek kullanımına yol açan sorunu buldum. Basit bir çözüm, Java-Engine'e (-Xmx) biraz daha fazla Yığın bellek vermek olabilir, ancak bu yalnızca uygulamanın daha önce yığın sınırı ayarlandığı kadar tam belleğe ihtiyacı varsa yardımcı olur.
Mnementh

1
@Mnementh burada bir cevap vermişti stackoverflow.com/questions/11091516/…
lulu

16
@SimonKuang Yığının OutOfMemoryErrorartırılmasının geçerli bir çözüm olmadığı birden fazla senaryo olduğunu unutmayın : yerel iş parçacıklarının bitmesi ve perm geninin (yığından ayrı olan) bitmesi iki örnektir. Aşağıdakiler hakkında aşırı geniş açıklamalarda bulunmaya dikkat edin OutOfMemoryErrors; onlara neden olabilecek beklenmedik şekilde çeşitli şeyler var.
Tim

3
Sorunu nasıl çözdün?
Thorsten Niehues

Yanıtlar:


763

Bu ileti, bir nedenden dolayı çöp toplayıcısının aşırı miktarda zaman aldığını (varsayılan olarak işlemin tüm CPU zamanının% 98'i) ve her çalıştırmada çok az bellek kurtardığını (varsayılan olarak yığının% 2'sini) kurtardığı anlamına gelir.

Bu, programınızın herhangi bir ilerleme kaydetmeyi durdurduğu ve her zaman yalnızca çöp toplama işlemini yürütmekle meşgul olduğu anlamına gelir.

Uygulamanızın herhangi bir şey yapmadan CPU zamanını emmesini önlemek için JVM Error, sorunu tanılama şansınız olması için bunu atar .

Bunun gerçekleştiğini gördüğüm nadir durumlar, bazı kodların zaten çok bellek kısıtlı bir ortamda tonlarca geçici nesne ve tonlarca zayıf referanslı nesne oluşturduğu yerdir.

Çeşitli Java sürümleri için mevcut olan ve bu sorunla ilgili bölümler içeren Java GC ayarlama kılavuzuna göz atın:


9
Cevabınızı şöyle özetlemek doğru olur mu: "Tıpkı bir 'Java Dizin Alanı yetersiz' hatası gibi. -Xmx ile daha fazla bellek verin." ?
Tim Cooper

58
@Tim: Hayır, bu doğru olmaz. Ona daha fazla bellek verirken olabilir sorunu azaltmak, ayrıca kod bakmak ve onu kod tarayan sadece "bellek yetersiz" işaretinin altındaysa o çöp miktarını ve neden üretir neden görmelisiniz. Genellikle bozuk kodun bir işaretidir.
Joachim Sauer

8
Teşekkürler, Oracle aslında veri taşımada o kadar iyi değil gibi görünüyor, bağlantıyı kopardılar.
Joachim Sauer

151
Beni "Teşekkürler, Oracle aslında o kadar da iyi değil" şeklinde
Rob Grant

3
@Guus: Birden fazla uygulama aynı JVM'de çalışıyorsa, evet, birbirlerini kolayca etkileyebilirler. Hangisinin yanlış davrandığını söylemek zor olacaktır. Uygulamaları farklı JVM'lere ayırmak en kolay çözüm olabilir.
Joachim Sauer

215

Oracle'ın "Java SE 6 HotSpot [tm] Sanal Makine Çöp Toplama Ayarı" makalesinden alıntı :

Aşırı GC Süresi ve OutOfMemoryError

Paralel toplayıcı, çöp toplamada çok fazla zaman harcanıyorsa bir OutOfMemoryError atar: toplam sürenin% 98'inden fazlası çöp toplama için harcanırsa ve yığının% 2'sinden azı kurtarılırsa, bir OutOfMemoryError atılır. Bu özellik, yığın çok küçük olduğu için çok az ilerleme kaydederken veya hiç ilerleme göstermezken uygulamaların uzun süre çalışmasını önlemek için tasarlanmıştır. Gerekirse, bu özellik -XX:-UseGCOverheadLimitkomut satırına seçenek eklenerek devre dışı bırakılabilir .

EDIT: Birisi benden daha hızlı yazabilir gibi görünüyor :)


87
"Bunu kapatabilirsin ..." ama muhtemelen OP bunu yapmamalı.
Stephen C

2
"-XX" ve "-Xmx" arasındaki farkı söyleyebilir misiniz? Ben de "-Xmx" seçeneğini kullanarak kapatmayı başardı.
Susheel Javadi

19
Burada çok eski bir yoruma yanıt veriyoruz, ama ... @Bart -XX:Birkaç komut satırı seçeneğinin başlangıcında, bu seçeneğin son derece VM'ye özgü ve kararsız olduğunu gösteren bir bayrak (gelecekteki sürümlerde önceden haber verilmeksizin değiştirilebilir). Her durumda, -XX:-UseGCOverheadLimitbayrak sanal makineye GC genel yük limiti kontrolünü (aslında "kapatır") devre dışı bırakmasını söylerken, -Xmxkomutunuz sadece yığını arttırır. İkinci durumda GC havai kontrolü hala çalışıyor , daha büyük bir yığın sizin durumunuzdaki GC thrashing sorunlarını çözmüş gibi görünüyor (bu her zaman yardımcı olmayacaktır).
Andrzej Doyle

1
Benim uygulamada (Talend büyük bir Excel dosyası okuma) bu işe yaramadı ve diğer kullanıcıların açıklama neden anlıyorum. Bu sadece hatayı devre dışı bırakır, ancak sorun devam eder ve uygulamanız GC'nin zamanının çoğunu harcayacaktır. Sunucumuz bol miktarda RAM vardı, bu yüzden Vitalii önerilerini yığın boyutunu artırmak için kullandım.
RobbZ

Uygulamanız veri yoğun ise, belleği temizlemek ve veri sızıntısını önlemek en iyi yoldur - ancak biraz zaman gerektirirse sonunda bu hatayı alırsınız.
Pievis

89

Programınızda bellek sızıntısı olmadığından eminseniz, şunları deneyin:

  1. Örneğin, yığın boyutunu artırın -Xmx1g.
  2. Eşzamanlı düşük duraklama toplayıcısını etkinleştirin -XX:+UseConcMarkSweepGC.
  3. Hafızadan tasarruf etmek için mevcut nesneleri mümkün olduğunca yeniden kullanın.

Gerekirse, komut satırına seçenek eklenerek sınır denetimi devre dışı bırakılabilir -XX:-UseGCOverheadLimit.


9
Üçüncü tavsiyeye katılmıyorum. Mevcut nesneleri yeniden kullanmak bellek tasarrufu (eski nesneleri sızıntı yok bellek tasarrufu :-) Ayrıca "mevcut nesneyi yeniden kullanmak" GC baskısını hafifletmek için bir uygulamadır. Ama her zaman iyi bir fikir DEĞİLDİR: modern GC ile, eski nesnelerin yenilerini tuttuğu durumlardan kaçınmalıyız, çünkü bazı yer varsayımlarını kırabilir ...
mcoolive

@mcoolive: Biraz uyuşmuş bir örnek için aşağıdaki stackoverflow.com/a/5640498/4178262 kodunu yanıtlamak için yorumlara bakın; Listnesnenin döngü içinde oluşturulması GC'nin 22 kez yerine 39 kez çağrılmasına neden oldu.
Mark Stewart

45

Genellikle kod. İşte basit bir örnek:

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 bit üzerinde Java 1.6.0_24-b07 kullanma.

java -Xloggc:gc.log GarbageCollector

Sonra bak gc.log

  • BAD yöntemi kullanılarak 444 kez tetiklendi
  • WORSE yöntemi kullanılarak 666 kez tetiklendi
  • BETTER yöntemi kullanılarak 354 kez tetiklendi

Şimdi, bu en iyi test veya en iyi tasarım değil, ancak böyle bir döngü uygulamaktan başka bir seçeneğiniz olmadığı veya kötü davranan mevcut kodla uğraşırken, yenilerini oluşturmak yerine nesneleri yeniden kullanmayı seçmek, çöp toplayıcının kaç kez girdiği ...


12
Lütfen açıklığa kavuşturun: "n kere tetiklendi" derken, bu normal bir GC'nin n kez gerçekleştiği veya OP tarafından bildirilen "GC genel sınır aşıldı" hatasının n kez gerçekleştiği anlamına mı geliyor?
Jon Schneider

Sadece java 1.8.0_91 kullanarak test ve asla bir hata / istisna var ve "Tetikledi n kez" gc.logdosyadaki satır sayısını saymak oldu . Testlerim genel olarak çok daha az kez, ancak DAHA İYİ için en az "Tetikleyici" kez gösteriyor ve şimdi, KÖTÜ şu anda KÖTÜDEN "kötü" Sayımlarım: BAD: 26, WORSE: 22, DAHA İYİ 21
Mark Stewart

Sadece tanımlamak bir "WORST_YET" modifikasyon eklendi List<Double> listiçinde dış döngü yerine önce dış döngü ve 39 çöp koleksiyonları Tetiklendi.
Mark Stewart

36

Java [8] Platformu, Standart Sürüm Sorun Giderme Kılavuzu'na göre hatanın nedeni : (vurgu ve satır kesmeleri eklendi)

[...] "GC havai limiti aşıldı", çöp toplayıcının her zaman çalıştığını ve Java programının çok yavaş ilerleme kaydettiğini gösterir.

Bir çöp toplama işleminden sonra, Java işlemi zamanının yaklaşık% 98'inden fazlasını çöp toplama işi yapıyorsa ve yığının% 2'sinden daha azını geri kazanıyorsa ve son 5 (derleme zaman sabiti) ardışık çöpü yapıyorsa sonra a java.lang.OutOfMemoryErroratılır. [...]

  1. Geçerli yığın yeterli değilse yığın boyutunu büyütün.
  2. Yığın belleği artırdıktan sonra hala bu hatayı alıyorsanız , MAT (Bellek analiz aracı), Visual VM vb. Gibi bellek profili oluşturma araçlarını kullanın ve bellek sızıntılarını düzeltin.
  3. JDK sürümünü en son sürüme (1.8.x) veya en az 1.7.x'e yükseltin ve G1GC algoritmasını kullanın. . G1 GC için üretim hedefi yüzde 90 uygulama süresi ve yüzde 10 çöp toplama süresidir
  4. - ile yığın hafızasını ayarlamak dışında Xms1g -Xmx2g,

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

G1GC ile ilgili daha fazla soruya göz atın


29

Sadece bu seçeneği ayarlayarak yığın boyutunu biraz artırın.

Çalıştır → Yapılandırmaları Çalıştır → Bağımsız Değişkenler → VM bağımsız değişkenleri

-Xms1024M -Xmx2048M

Xms - minimum sınır için

Xmx - maksimum sınır için


2
Android uygulamaları argumentssekmesi yok ... Bunu başarmak için ne yapmalıyız?
Blaze Tama

3
Bu cevap ne için? Bu bir Tutulma sorusu değildi.
Michael Piefel

3
"Minimum sınır" yoktur. -Xms başlangıç ​​boyutudur.
Diego Queiroz

1
Ayarlanabilecek maksimum sınırın maksimum değeri nedir?
JPerk

14

Benim için aşağıdaki adımlar işe yaradı:

  1. eclipse.iniDosyayı aç
  2. Değişiklik

    -Xms40m
    -Xmx512m

    için

    -Xms512m
    -Xmx1024m
  3. Tutulmayı Yeniden Başlat

Buraya bakın


bu sorunu çözmenin en basit yolu. Teşekkürler :)
Hamza

1
jdev'deki eclipse.ini dosyası?
Abhinaba Basu

konfigürasyon bu şekilde değiştirilse bile problemler çözülmedi.
zionpi

1
OP bir Eclipse sorusu sormadı.
Michael Piefel

1
Bu "cevap" yukarıdaki soruya cevap vermiyor.
Freitags

13

bunu dene

açmak build.gradledosyayı

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

Simülatör için harika çalışıyor. Bunun gerçek cihazları nasıl etkilediğine dair bir fikrin var mı? yani bu iyi bir fikir mi yoksa sadece sorunu maskeliyor mu? Teşekkürler.
Joshua Pinter

11

Aşağıdakiler benim için çalıştı. Aşağıdaki snippet'i eklemeniz yeterlidir:

android {
        compileSdkVersion 25
        buildToolsVersion '25.0.1'

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

Evet, Gradle kullanırken :)
Alex

4
Bunun genel olarak sorusuna bir çözüm olduğunu nasıl düşünüyorsunuz ? Android facepalm için bir yığın yapılandırmasında tamamen keyfi olan yığın boyutunuzu 4g olarak ayarlarsınız .
Julian L.

7

build.gradle (Modül: app) dosyanızdaki javaMaxHeapsize değerini artırın

dexOptions {
    javaMaxHeapSize "1g"
}

(Bu satırı dereceye ekle)

 dexOptions {
        javaMaxHeapSize "4g"
    }

3

Java yığın boyutu açıklamaları (xms, xmx, xmn)

-Xms size in bytes

Example : java -Xms32m

Java yığınının başlangıç ​​boyutunu ayarlar. Varsayılan boyut 2097152'dir (2 MB). Değerler 1024 baytın (1KB) katı ve daha büyük olmalıdır. (-Server bayrağı varsayılan boyutu 32M'ye çıkarır.)

-Xmn size in bytes

Example : java -Xmx2m

Eden nesli için ilk Java yığın boyutunu ayarlar. Varsayılan değer 640K'dır. (-Server bayrağı varsayılan boyutu 2M'ye çıkarır.)

-Xmx size in bytes

Example : java -Xmx2048m

Java yığınının büyüyebileceği maksimum boyutu ayarlar. Varsayılan boyut 64M'dir. (-Server bayrağı varsayılan boyutu 128M'ye çıkarır.) Maksimum yığın sınırı yaklaşık 2 GB'dir (2048MB).

Java bellek değişkenleri (xms, xmx, xmn) biçimlendirme

Java yığın boyutunu ayarlarken, MB için “m” veya “M” veya GB için “g” veya “G” harflerinden birini kullanarak bellek değişkeninizi belirtmelisiniz. “MB” veya “GB” belirtirseniz ayarınız çalışmaz. Geçerli argümanlar şöyle görünür:

-Xms64m veya -Xms64M -Xmx1g veya -Xmx1G 2 GB belirtmek için 2048 MB da kullanabilir. Ayrıca, bağımsız değişkenlerinizi belirtirken yalnızca tam sayıları kullandığınızdan emin olun. -Xmx512m kullanmak geçerli bir seçenektir, ancak -Xmx0.5g hataya neden olur.

Bu referans birisi için faydalı olabilir.


2

Bunu gradle.propertiesdosyanıza ekleyerek bellek ayırma ve yığın boyutunu da artırabilirsiniz :

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

2048M ve 32g olması gerekmez, istediğiniz kadar büyük yapın.


2

Çözüldü:
Sadece eklemek
org.gradle.jvmargs=-Xmx1024m
içinde
gradle.properties
ve yoksa, oluşturun.


0

Android Studio'da çalışıyorum ve sürüm için imzalı bir APK oluşturmaya çalışırken bu hatayla karşılaştım. Bir hata ayıklama APK'sını sorunsuz bir şekilde oluşturup test edebildim, ancak bir sürüm APK'sı oluşturmak istediğimde, derleme işlemi dakikalarca çalışacak ve daha sonra "Hata java.lang.OutOfMemoryError: GC ile sonlandırılacak. msgstr "genel gider sınırı aşıldı". Hem VM hem de Android DEX derleyicisi için yığın boyutlarını artırdım, ancak sorun devam etti. Son olarak, saatlerce ve kahve fincanlarından sonra, sorunun uygulama düzeyindeki 'build.gradle' dosyamda olduğu ortaya çıktı - sonuç olarak Proguard sayfalarını çalıştıran yayın oluşturma türü için 'minifyEnabled' parametresine sahiptim. kod küçültme sürecinden geçmeyen kodda (bkz. https://developer.android.). 'MinifyEnabled' parametresini 'true' olarak değiştirdim ve sürüm derlemesi rüya gibi yürütüldü :)

Kısacası, uygulama düzeyinde 'build.gradle' dosyamı: // ...

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

//...

için

    //...

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'daki yığın boyutunu artırmak için aşağıdaki talimatları izleyin. Benim için çalıştı.

Windows Kullanıcıları için,

IDE'nin kurulu olduğu konuma gidin ve aşağıdakileri arayın.

idea64.exe.vmoptions

Dosyayı düzenleyin ve aşağıdakileri ekleyin.

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

Bu kadar !!


0

bu görüntüye bakarak sunucu ayarında değişiklik yapmayı deneyebilir ve sarı renkle vurgulanmış işlem değişikliklerinin bellek boyutunu artırabilirsiniz

set _java_opts -Xmx2g
programınızın karmaşıklığına bağlı olarak cmd-> 2g (2 gigabayt) açarak java yığınında da değişiklik yapabilirsiniz

daha az sabit değişken ve geçici değişkenler kullanmaya çalışın

resim açıklamasını buraya girin


-1

Jdeveloper'da bellek boyutunu artırmanız gerekiyor setDomainEnv.cmd adresine gidin .

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

ve

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
Bu ayarlar yalnızca yerel IDE'nize özgüdür. Bu, Prod ortamı için işe yaramaz.
Feng

-1

Netbeans'te, maksimum yığın boyutu tasarlamak yararlı olabilir. Go Run => Set Projesi Yapılandırma => Özelleştir . In the Run onun açıldı pencerenin, gidin VM Seçeneği , içinde dolgu -Xms2048m -Xmx2048m. Yığın boyutu sorunu çözebilir.


-1

MacBook'umu yeniden başlatmak bu sorunu benim için düzeltti.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.