“Java.lang.OutOfMemoryError: Java yığın alanı” hatasıyla nasıl başa çıkılır?


416

Java 5 üzerinde bir istemci tarafı Swing uygulaması (grafik yazı tipi tasarımcısı) yazıyorum . Son zamanlarda, bellek kullanımı konusunda muhafazakar olmadığım için hata yapıyorum. Kullanıcı sınırsız sayıda dosya açabilir ve program açılan nesneleri bellekte tutar. Hızlı bir araştırmadan sonra 5.0 Java Sanal Makinesi'nde Ergonomi buldum ve diğerleri Windows makinesinde JVM varsayılan olarak maksimum yığın boyutunu varsayılan olarak söylüyor .java.lang.OutOfMemoryError: Java heap space64MB

Bu durum göz önüne alındığında, bu kısıtlamayla nasıl başa çıkmalıyım?

Java için komut satırı seçeneğini kullanarak maksimum yığın boyutunu artırabilir , ancak bu kullanılabilir RAM bulmak ve bazı başlatma programı veya komut dosyası yazma gerektirir. Ayrıca, bazı sonlu maksimuma yükselmek nihayetinde sorundan kurtulmaz.

Belleği boşaltmak için (veritabanı kullanarak aynı şeydir) sık sık dosya sistemine nesneleri kalıcı olarak kodumu yeniden yazabilirsiniz. İşe yarayabilir, ama muhtemelen çok fazla iş vardır.

Beni yukarıdaki fikirlerin ayrıntılarına veya yığın boyutunu dinamik olarak genişleten otomatik sanal bellek gibi bazı alternatiflere yönlendirebilirseniz , bu harika olacaktır.


Varsayılan maksimum yığın boyutu 64 MB, J2SE 5.0 öncesindendir. J2SE 8.0 bilgileri için docs.oracle.com/javase/8/docs/technotes/guides/vm/… adresindeki "Çöp Toplayıcı Ergonomi" konusuna bakın .
Andy Thomas

Her bir OOM sorusu bu soruya kopyalandığı için buraya geldiyseniz, şunları da kontrol ettiğinizden emin olun: stackoverflow.com/questions/299659/… OOM'dan önce 'tam zamanında' bellek referanslarını temizlemek için çözüm sağlar. SoftReferences gerçek sorununuzu çözen bir araç olabilir.
Steve Steiner

Yanıtlar:


244

Sonuçta, hangi platformda çalışırsanız çalışın, her zaman kullanabileceğiniz sınırlı bir yığın yığınınız olur. Windows 32 bit'te bu 2GB(özellikle yığın değil, işlem başına toplam bellek miktarı) civarındadır. Sadece Java varsayılanı daha küçük yapmayı seçer (muhtemelen programcı bu problemle karşılaşmadan ve tam olarak ne yaptıklarını incelemek zorunda kalmadan kaçak bellek tahsisi olan programlar oluşturamaz).

Bu nedenle, hangi bellek miktarına ihtiyacınız olduğunu belirlemek veya kullandığınız bellek miktarını azaltmak için kullanabileceğiniz birkaç yaklaşım vardır. Java veya C # gibi çöp toplanan dillerle ilgili yaygın bir hata, artık kullanmadığınız nesnelere yapılan referansları saklamak veya bunun yerine birçok nesneyi yeniden kullanabileceğiniz zaman ayırmaktır . Nesnelerin bunlara referansları olduğu sürece, çöp toplayıcı bunları silmeyeceği için yığın alanını kullanmaya devam ederler.

Bu durumda, programınızdaki hangi yöntemlerin çok sayıda nesne ayırdığını belirlemek için bir Java bellek profili oluşturucusu kullanabilir ve daha sonra artık başvurulmadığından emin olmanın bir yolu olup olmadığını veya ilk etapta ayırmamalarını belirleyebilirsiniz. Geçmişte kullandığım bir seçenek "JMP" http://www.khelekore.org/jmp/ .

Bu nesneleri bir nedenden dolayı tahsis ettiğinizi belirlerseniz ve referansları saklamanız gerektiğinde (bu durumda ne yaptığınıza bağlı olarak), programı başlattığınızda yalnızca maksimum yığın boyutunu artırmanız gerekir. Ancak, bellek profili oluşturduktan ve nesnelerinizin nasıl ayrıldığını anladıktan sonra, ne kadar belleğe ihtiyacınız olduğu hakkında daha iyi bir fikriniz olmalıdır.

Genel olarak, programınızın sınırlı miktarda bellekte çalışacağını garanti edemiyorsanız (belki de giriş boyutuna bağlı olarak) her zaman bu sorunla karşılaşırsınız. Ancak tüm bunları bitirdikten sonra diske vb. Önbellekleme nesnelerine bakmanız gerekir. Bu noktada bir şey için "Xgb belleğe ihtiyacım var" demek için çok iyi bir nedeniniz olmalı ve bunu iyileştirerek çalışamazsınız. algoritmalarınız veya bellek ayırma düzenleriniz. Genellikle bu genellikle büyük veri kümelerinde (veritabanı veya bazı bilimsel analiz programları gibi) çalışan algoritmalar için geçerli olur ve daha sonra önbellekleme ve bellek eşlemli G / Ç gibi teknikler yararlı olur.


6
OpenJDK ve OracleJDK profiler - jvisualvm'yi bir araya getirdi. Daha fazla kolaylık istiyorsanız ticari Yourkit öneririm.
Petr Gladkikh

121

Java'yı , yığının maksimum boyutunu -Xmxayarlayan komut satırı seçeneğiyle çalıştırın .

Ayrıntılar için buraya bakın .


3
Bu parametre sonsuza kadar nasıl ayarlanır? Çünkü 'gradlew assemble' komutunu kullanıyorum.
Dr.jacky

2
Çalıştır-> Çalıştırma Konfigürasyonları-> Argümanları tıklayın-> VM argümanlarının içinde -Xms1g -Xmx2g
Arayan Singh

2
Gerçek cevap bu.
nccc

85

Proje başına projenizin ne kadar yığın alanı istediğini belirtebilirsiniz

İçin aşağıda Eclipse Helios / Juno / Kepler :

Farenin sağ tuşuyla tıklayın

 Run As - Run Configuration - Arguments - Vm Arguments, 

o zaman bunu ekle

-Xmx2048m

1
merhaba bighostkim ve cuongHuyTo, nerede "Arguments" .. i Yapılandırma çalıştırmak kadar görebilirsiniz. Lütfen beni ara. Gmail'den yaklaşık 2000 kişi indirip saklamam gerekiyor. Bellek yetersiz özel durumu nedeniyle çöküyor
AndroidRaji

@AndroiRaji: farenizi çalıştırılabilir ana (yani "public static void main (String [] args)") olan Java sınıfına sağ tıklatıp Farklı Çalıştır - Yapılandırmayı Çalıştır'ı seçin. Sonra "Bağımsız Değişkenler", Ana'dan sonraki sekmedir (Ana, Bağımsız Değişkenler, JRE, Sınıfyolu, Kaynak, Çevre, Ortak sekmelerini görürsünüz).
CuongHuyTo

47

Yığın boyutunu arttırmak bir "düzeltme" değildir,% 100 geçici bir "sıva" dır. Başka bir yerde tekrar çökecek. Bu sorunları önlemek için yüksek performanslı kod yazın.

  1. Mümkün olan yerlerde yerel değişkenleri kullanın.
  2. Doğru nesneyi seçtiğinizden emin olun (EX: String, StringBuffer ve StringBuilder arasında seçim)
  3. Programınız için iyi bir kod sistemi kullanın (EX: Statik değişkenleri VS statik olmayan değişkenleri kullanma)
  4. Kodunuzda çalışabilecek diğer şeyler.
  5. Çok fazla İPLİKLE hareket etmeye çalışın

Bu çok doğru. AWT iş parçacığında OOM alıyorum bir sorunu düzeltmeye çalışıyorum ama farklı yeni iş parçacığı kullanırsanız, OOM sorunu almıyorum. Online bulabildiğim tek AWT iplik için yığın boyutunu artırmak olduğunu.
Ashish

@Ash: Evet, sıva aramak yerine temel sorunu düzeltin.
Limon Suyu

Çöp toplama ve Java'daki bellek yönetimi yaklaşımının öncüllerinin tüm bu malloc-dealloc komplikasyonlarını çözmesi gerekiyordu :( Elbette bu cevaba tamamen katılıyorum, varsayılanlar yalın verilerle kod yazmayı kolay hale getirmiyor
Davos

31

Büyük uyarı ---- ofisimde, (bazı Windows makinelerinde) Java yığını için 512 metreden fazla ayıramadığımızı buluyorduk. Bunun nedeni, bu makinelerin bazılarına yüklenen Kaspersky antivirüs ürünüydü. Bu AV ürününü kaldırdıktan sonra, en az 1.6gb tahsis edebileceğimizi bulduk, yani -Xmx1600m(m zorunludur, başka bir hataya "Çok küçük başlangıç ​​yığını" neden olur) çalışır.

Bunun diğer AV ürünlerinde olup olmadığı hakkında bir fikir yok, ancak muhtemelen bu gerçekleşiyor çünkü AV programı her adres alanında küçük bir bellek bloğu ayırıyor, böylece tek bir büyük ayırmayı önlüyor.


22

VM argümanları benim için tutulmada işe yaradı. Tutulma sürüm 3.4 kullanıyorsanız aşağıdakileri yapın

gidin Run --> Run Configurations -->> ardından sekmesi "JRE" seç - - o zaman maven yapı altında projeyi seçin> enter -Xmx1024m.

Alternatif olarak şunu Run --> Run Configurations --> select the "JRE" tab -->girebilirsiniz -Xmx1024m

Bu, tüm yapılar / projeler için bellek yığınını artırmalıdır. Yukarıdaki bellek boyutu 1 GB'dir. İstediğiniz şekilde optimize edebilirsiniz.


18

Evet, -XmxJVM'niz için daha fazla bellek yapılandırabilirsiniz. Bellek sızdırmadığından veya boşa harcamadığınızdan emin olmak için. Bir yığın dökümü yapın ve bellek tüketiminizi analiz etmek için Eclipse Memory Analyzer'ı kullanın.


JVMJ9VM007E Komut satırı seçeneği tanınmadı: -Xmx Java sanal makinesi oluşturulamadı. Downvote
Philip Rego

17

Oracle sorun giderme ile ilgili öneriler eklemek istiyorum makalesinden .

İş parçacığı_adı iş parçacığında özel durum: java.lang.OutOfMemoryError: Java yığın alanı

Java yığın alanı ayrıntı iletisi, nesnenin Java yığınına ayrılamadığını gösterir. Bu hata mutlaka bellek sızıntısı anlamına gelmez

Olası nedenler:

  1. Belirtilen yığın boyutunun uygulama için yetersiz olduğu basit yapılandırma sorunu .

  2. Uygulama istemeden nesnelere referanslar tutar ve bu nesnelerin çöp toplanmasını önler.

  3. Sonlandırıcıların aşırı kullanımı .

Bu hatanın başka bir potansiyel kaynağı, sonlandırıcıları aşırı kullanan uygulamalarda ortaya çıkar. Bir sınıfın bir sonlandırma yöntemi varsa, bu tür nesnelerin boşlukları çöp toplama zamanında geri alınmaz

Çöp toplandıktan sonra , nesneler daha sonra gerçekleşecek olan sonlandırma için sıraya alınır . sonlandırıcılar sonlandırma sırasına hizmet eden bir daemon iş parçacığı tarafından yürütülür. Eğer Finalizer iplik sonuçlandırma kuyruğunda ile takip edemiyorum, daha sonra Java yığın doldurmak olabilir ve bu tip OutOfMemoryError istisna atılmış olacaktır.

Bu duruma neden olabilecek bir senaryo, bir uygulamanın , sonlandırma sırasının, sonlandırıcı iş parçacığının bu kuyruğa hizmet verme hızından daha hızlı bir oranda artmasına neden olan yüksek öncelikli iş parçacıkları oluşturmasıdır .


9

Aşağıdaki adımları izleyin:

  1. catalina.shtomcat / bölmesinden.

  2. JAVA_OPTS değerini şununla değiştir:

    JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1536m 
    -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m 
    -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
  3. Tomcat'inizi yeniden başlatın


8

Deneyebileceğiniz başka bir yerde okudum - catch java.lang.OutOfMemoryError ve catch bloğunda, çok fazla bellek kullanabileceğini, bağlantılarını vb. Kullanabileceğinizi bildiğiniz tüm kaynakları serbest bırakabilirsiniz. System.gc() sonra tekrar deneyin yapacaktın.

Başka bir yolu bu olsa da, bu işe yarayıp yaramayacağını bilmiyorum, ama şu anda benim uygulama üzerinde çalışıp çalışmayacağını test ediyorum.

Fikir, boş belleği artırdığı bilinen System.gc () 'yi çağırarak Çöp toplama yapmaktır. Bellek yavaşlatma kodu yürütüldükten sonra bunu kontrol etmeye devam edebilirsiniz.

//Mimimum acceptable free memory you think your app needs
long minRunningMemory = (1024*1024);

Runtime runtime = Runtime.getRuntime();

if(runtime.freeMemory()<minRunningMemory)
 System.gc();

6
Genel olarak JVM'nin bir OutOfMemoryError atmak yerine Çöp Toplama'yı (GC) tercih edeceğini düşünüyorum. OutOfMemoryError'dan sonra açıkça System.gc () çağrılması bazı VM'lere / yapılandırmalara yardımcı olabilir, ancak genel durumda çok iyi çalışmasını beklemezdim. Ancak, gereksiz nesne referanslarını bırakmak neredeyse tüm durumlarda kesinlikle yardımcı olacaktır.
Mike Clark

6
@mwangi Calling System.gc () doğrudan koddan genellikle kötü bir fikirdir. JVM'ye GC'nin yapılması gerektiğine dair bir öneri , ancak kesinlikle gerçekleştirileceğinin garantisi yok .

7

Java'da çözmenin kolay yolu, OutOfMemoryErrorJVM seçeneklerini kullanarak maksimum yığın boyutunu artırmaktır -Xmx512M, bu da OutOfMemoryError'unuzu hemen çözecektir. Proje oluştururken Eclipse, Maven veya ANT'de OutOfMemoryError'u aldığımda tercih edilen çözümüm, çünkü projenin boyutuna bağlı olarak kolayca Bellek tükenebilir.

Burada, JVM'nin maksimum yığın boyutunu artırmanın bir örneği, Java uygulamanızda yığın boyutunu ayarlıyorsanız -Xmx ila -Xms oranını 1: 1 veya 1: 1.5 olarak tutmak daha iyidir.

export JVM_ARGS="-Xms1024m -Xmx1024m"

Referans Bağlantısı


1
Onları neden 1: 1 veya 1: 1.5 oranında tutmamız gerektiğine dair bir fikrin var mı?
ernesto

7

Geliştirme için varsayılan olarak JVM, performansla ilgili diğer özellikler için küçük boyutlu ve küçük yapılandırma kullanır. Ancak üretim için örneğin (Uygulama Sunucusuna özel yapılandırma mevcut olabilir) -> (İsteği yerine getirmek için hala yeterli bellek yoksa ve yığın zaten maksimum boyuta ulaştıysa, bir OutOfMemoryError oluşacaktır)

-Xms<size>        set initial Java heap size
-Xmx<size>        set maximum Java heap size
-Xss<size>        set java thread stack size

-XX:ParallelGCThreads=8
-XX:+CMSClassUnloadingEnabled
-XX:InitiatingHeapOccupancyPercent=70
-XX:+UnlockDiagnosticVMOptions
-XX:+UseConcMarkSweepGC
-Xms512m
-Xmx8192m
-XX:MaxPermSize=256m (in java 8 optional)

Örneğin: Linux Platformunda üretim modu için tercih edilen ayarlar.

Sunucuyu bu şekilde indirip yapılandırdıktan sonra http://www.ehowstuff.com/how-to-install-and-setup-apache-tomcat-8-on-centos-7-1-rhel-7/

1. / opt / tomcat / bin / klasöründe setenv.sh dosyası oluşturun

   touch /opt/tomcat/bin/setenv.sh

2. tercih edilen modu ayarlamak için bu parametreleri açın ve yazın.

nano  /opt/tomcat/bin/setenv.sh 

export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=8"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+CMSClassUnloadingEnabled"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=70"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UnlockDiagnosticVMOptions"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseConcMarkSweepGC"
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx8192m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=256M"

3.service tomcat restart

JVM'nin yalnızca yığından daha fazla bellek kullandığını unutmayın. Örneğin, Java yöntemleri, iş parçacığı yığınları ve yerel tanıtıcılar bellekte yığıntan ayrı olarak JVM dahili veri yapılarına ayrılır.


7

Java yığın boyutu aynı sorunla karşı karşıya.

Java 5 (1.5) kullanıyorsanız iki çözümüm var.

  1. sadece jdk1.6'yı yükleyin ve tutulmanın tercihlerine gidin ve yüklediğiniz gibi jav1 1.6'nın jre yolunu ayarlayın.

  2. VM argümanınızı kontrol edin ve her ne olursa olsun olsun. VM argümanlarında mevcut tüm argümanların bir satırını -Xms512m -Xmx512m -XX olarak eklemeniz yeterlidir: MaxPermSize = ... m (192m).

Bence işe yarayacak...


7

Bellek kullanımınızı çalışma zamanında izlemeniz gerekiyorsa, java.lang.managementpaketMBeans , VM'nizdeki bellek havuzlarını (örn. Eden alanı, dayanıklı üretim vb.) Ve ayrıca çöp toplama davranışını izlemek için kullanılabilen .

Bu MBeans tarafından bildirilen boş yığın alanı, özellikle uygulamanız daha sonra GC-ed olan çok sayıda nesne oluşturuyorsa, GC davranışına bağlı olarak büyük ölçüde değişecektir. Olası bir yaklaşım, her tam GC'den sonra, yığın nesneleri sürerek belleği boşaltmaya karar vermek için kullanabileceğiniz boş yığın alanını izlemektir.

Nihayetinde, en iyi seçim performans kabul edilebilir kalırken bellek elde tutmayı mümkün olduğunca sınırlamaktır. Bir önceki yorumun belirttiği gibi, bellek her zaman sınırlıdır, ancak uygulamanızın bellek tükenmesi ile ilgili bir stratejisi olmalıdır.


5

Bir dağıtım durumunda buna ihtiyacınız varsa, Java WebStart'ı ("ondisk" sürümüyle değil, ağa değil - Java 6u10 ve sonraki sürümlerde mümkündür) bir çaprazda JVM'ye yönelik çeşitli bağımsız değişkenleri belirtmenize izin verdiğini unutmayın. platform yolu.

Aksi takdirde, ihtiyacınız olan argümanları ayarlayan işletim sistemine özgü bir başlatıcıya ihtiyacınız olacaktır.


Java WebStart kullanımdan kaldırılıyor. Henüz uygun bir yedek parçanın farkında değilim.
Thorbjørn Ravn Andersen

1

Bu sorun Wildfly 8 ve JDK1.8'de oluyorsa, PermGen ayarları yerine MaxMetaSpace ayarlarını belirtmemiz gerekir.

Örneğin, wildfly'ın setenv.sh dosyasına aşağıdaki yapılandırmayı eklememiz gerekir. JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"

Daha fazla bilgi için lütfen Wildfly Öbek Sorunu'na bakın


1

Netbeans ile ilgili olarak, sorunu çözmek için maksimum yığın boyutunu ayarlayabilirsiniz.

'Çalıştır'a gidin, ardından ->' Proje Yapılandırmasını Ayarla '->' Özelleştir '-> açılan penceresinin' çalıştır '->' VM Seçeneği '->' -Xms2048m -Xmx2048m 'doldurun .


1

Nesneye referansları ayırmaya ve saklamaya devam ederseniz, sahip olduğunuz belleği dolduracaksınız.

Seçeneklerden biri, sekmeleri değiştirdiklerinde saydam bir dosyayı kapatıp açmaktır (yalnızca dosyaya bir işaretçi tutarsınız ve kullanıcı sekmeyi değiştirdiğinde, tüm nesneleri kapatır ve temizlersiniz ... dosyanın daha yavaş değişmesini sağlar ... ancak ...) ve belki de yalnızca 3 veya 4 dosyayı hafızada saklayın.

Yapmanız gereken başka bir şey, kullanıcı bir dosyayı açtığında, yüklediğinde ve herhangi bir OutOfMemoryError'a müdahale ettiğinde, (dosyayı açmak mümkün olmadığından) bu dosyayı kapatın, nesnelerini temizleyin ve kullanılmayanı kapatması gerektiği konusunda kullanıcıyı uyarın Dosyalar.

Sanal belleği dinamik olarak genişletme fikriniz sorunu çözmez, çünkü makine kaynaklarla sınırlıdır, bu nedenle bellek sorunlarını dikkatli ve ele almalısınız (veya en azından bunlara dikkat edin).

Bellek sızıntıları ile ilgili gördüğüm birkaç ipucu:

-> Bir koleksiyona bir şey koyarsanız ve daha sonra unutursanız, yine de güçlü bir referansınız olduğunu unutmayın, bu yüzden koleksiyonu geçersiz kılın, temizleyin veya onunla bir şeyler yapın ... bellek sızıntısı bulmak zor.

-> Belki, bellek sorunları ile yardımcı olabilir, ancak zayıf referanslar (weakhashmap ...) ile koleksiyonları olabilir kullanarak gerekir Eğer aradığınız nesne toplandığında olduğunu görebilirsiniz için, onunla dikkatli ol.

-> Bulduğum başka bir fikir, en az kullanılan ve şeffaf olarak yüklenen veritabanı nesnelerinde depolanan kalıcı bir koleksiyon geliştirmektir. Bu muhtemelen en iyi yaklaşım olacaktır ...


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.