Java'da bellek nasıl boşaltılır?


148

Java'da hafızayı C'nin free()işlevine benzer şekilde boşaltmanın bir yolu var mı ? Yoksa nesneyi null olarak ayarlamak ve tek seçenek GC'ye güvenmek mi?


153
Tamam ... bir şeyi açıklığa kavuşturalım. Sırf bir şeyin kötü bir uygulama olduğunu ve yapmayı teşvik edecek bir şey olmadığını düşünmeniz, onu olumsuz oylamaya değer yapmaz. Bu açık ve geçerli bir sorudur, Java'da hafızayı çöp toplamaya dayanmadan serbest bırakmanın bir yolu olup olmadığını sorar. Cesareti kırılmış ve genellikle yararlı ya da iyi bir fikir olmasa da, Felix'in bildiklerini bilmeden gerekebilecek senaryolar olmadığını bilemezsiniz. Felix onu kullanmayı planlamıyor bile olabilir. Bunun mümkün olup olmadığını bilmek isteyebilir. Hiçbir şekilde bir oylamayı hak etmiyor.
Daniel Bingham,

7
Açıklığa kavuşturmak için, bu, bunu kime oy verdiyse hedefliyor - önceki yorumları değil.
Daniel Bingham

Yanıtlar:


96

Java, yönetilen belleği kullanır, bu nedenle belleği newayırmanın tek yolu operatörü kullanmaktır ve belleği serbest bırakmanın tek yolu çöp toplayıcıya güvenmektir.

Bu bellek yönetimi teknik raporu (PDF), neler olduğunu açıklamaya yardımcı olabilir.

System.gc()Çöp toplayıcının hemen çalışmasını önermek için de arayabilirsiniz . Ancak, nihai kararı kodunuz değil Java Runtime verir.

Göre Java belgeleri ,

Gc yönteminin çağrılması, Java Sanal Makinesi'nin şu anda kapladıkları belleği hızlı bir şekilde yeniden kullanılabilir hale getirmek için kullanılmayan nesneleri geri dönüştürmek için çaba harcadığını gösterir. Yöntem çağrısından kontrol geri döndüğünde, Java Sanal Makinesi, atılan tüm nesnelerden alan kazanmak için en iyi çabayı göstermiştir.


5
Çöp Toplayıcıyı çalışmaya zorlar. Yine de hafızayı boşaltmaya zorlamaz ...
Pablo Santa Cruz

14
Hayır Pablo, GC'yi çalışmaya zorlamaz.
Jesper,

1
Çok güvenilir bir kişi tarafından bana, tüm HotSpotVM çöp toplayıcılarının System.gc()tamamen görmezden geldiği söylendi .
Esko

1
WinXp'de java SE GC, her System.gc () veya hemen hemen her şeyi çalıştırır, ancak API belgesi bunu garanti etmez.
teodozjan

2
@Pablo Santa Cruz Belleği boşaltmıyor ne demek istiyorsun? Bir sızıntısı var gibi görünen programımda test ettim ve ram kullanımı stabilize görünüyordu? Ve Daniel sadece şunu önerdiğini söyledi, o zaman nasıl oluyor da, yöntemi her çağırdığımda kullanılan ram yüzdesi her zaman dengelendi. Kafamı karıştırıyorsunuz.

65

Hiç kimse null, düşünmek isteyebileceğiniz hafızayı "serbest bırakmak" için meşru bir teknik olan nesne referanslarını açıkça belirtmekten bahsetmemiş gibi görünüyor .

Örneğin, List<String>boyutu çok büyük olan, ancak yalnızca yöntemin yarısına kadar gerekli olan bir yöntemin başında bir ilan ettiğinizi varsayalım . Bu noktada List referansını null, yöntem tamamlanmadan önce çöp toplayıcının bu nesneyi potansiyel olarak geri almasına izin verecek şekilde ayarlayabilirsiniz (ve referans yine de kapsam dışına düşer).

Bu tekniği gerçekte nadiren kullandığımı ancak çok büyük veri yapılarıyla uğraşırken dikkate almaya değer olduğunu unutmayın.


8
Bir yöntemin sadece bir kısmı için kullanılan bir nesne üzerinde gerçekten çok çalışma yapıyorsanız, ikisini de öneririm; yönteminiz fazla derlenmiş, yöntemi önce ve sonra bölümlerine ayırın veya kodun ilk yarısı için bir blok kullanın (daha sonra test komut dosyaları için daha kullanışlıdır)
Peter Lawrey

5
Bir nesne başvurusunun null değerine ayarlanmasının önemli olduğu yer, başka bir uzun ömürlü nesneden (veya muhtemelen statik bir var'dan) başvurulduğu zamandır. Örneğin, uzun ömürlü büyük nesneler dizisine sahipseniz ve bu nesnelerden birini kullanmayı bırakırsanız, nesneyi GC için kullanılabilir hale getirmek için dizi referansını null olarak ayarlamalısınız.
Hot Licks

22
System.gc(); 

Çöp toplayıcıyı çalıştırır.

Gc yöntemin çağrılması anlaşılacağı bellek yapmak için kullanılmayan nesneleri geri dönüşüm doğru Java Virtual Machine expend çaba şu anda hızlı yeniden kullanılabilecektir işgal söyledi. Yöntem çağrısından kontrol geri döndüğünde, Java Sanal Makinesi, atılan tüm nesnelerden alan kazanmak için en iyi çabayı göstermiştir.

Tavsiye edilmez.

Düzenleme: Orijinal cevabı 2009'da yazdım. Şimdi 2015.

Çöp toplayıcıları, Java'nın yaklaşık 20 yılı içinde giderek daha iyi hale geldi. Bu noktada, çöp toplayıcıyı manuel olarak çağırıyorsanız, diğer yaklaşımları düşünmek isteyebilirsiniz:

  • Sınırlı sayıda makinede GC'yi zorluyorsanız , mevcut makineden uzakta bir yük dengeleyici noktası bulundurmaya, bağlı istemcilere hizmet vermeyi bitirmesini beklemeye, bağlantıların askıya alınması için bir süre sonra zaman aşımına uğramaya ve daha sonra zorlamaya değer olabilir. - JVM'yi yeniden başlatın. Bu korkunç bir çözüm, ancak System.gc () 'ye bakıyorsanız, zorunlu yeniden başlatmalar olası bir engel olabilir.
  • Farklı bir çöp toplayıcı kullanmayı düşünün. Örneğin, (son altı yılda yeni olan) G1 toplayıcı düşük duraklamalı bir modeldir; genel olarak daha fazla CPU kullanır, ancak yürütmeyi asla zorlamamak en iyisidir. Sunucu CPU'larının artık hemen hemen hepsinin birden fazla çekirdeği olduğundan, bu gerçekten iyi bir değiş tokuş olabilir.
  • Bellek kullanımını ayarlayan bayraklarınıza bakın. Özellikle Java'nın daha yeni sürümlerinde, uzun süreli çalışan bu kadar çok nesneniz yoksa, yığındaki yeni gen boyutunu artırmayı düşünün. newgen (genç), yeni nesnelerin tahsis edildiği yerdir. Bir web sunucusu için, bir istek için oluşturulan her şey buraya yerleştirilir ve bu alan çok küçükse, Java nesneleri öldürmenin daha pahalı olduğu daha uzun ömürlü belleğe yükseltmek için fazladan zaman harcar. (Yeni nesil biraz fazla küçükse, bunun bedelini ödeyeceksiniz.) Örneğin, G1'de:
    • XX: G1NewSizePercent (varsayılan değer 5; muhtemelen önemli değil.)
    • XX: G1MaxNewSizePercent (varsayılan 60'tır; muhtemelen bunu artırın.)
  • Çöp toplayıcısına daha uzun bir duraklama ile sorun yaşamadığınızı söyleyin. Bu, sistemin geri kalan kısıtlamalarını korumasına izin vermek için daha sık GC çalıştırmalarına neden olacaktır. G1'de:
    • XX: MaxGCPauseMillis (varsayılan olarak 200'dür.)

1
Kendi gönderime yorum yaptığımda, bu genellikle hiçbir şey yapmaz ve onu tekrar tekrar çağırmak JVM'nin kararsız hale gelmesine neden olabilir. Ayrıca köpeğinizin üzerinden geçebilir; dikkatli yaklaşın.
Dean J

1
"Gc yöntemini çağırmak, JVM'nin çabayı genişletmesini öneriyor" un "öneriler" kısmına çok vurgu yapacağım
matt b

2
@Jesper, Dean'in cevabı "öneriyor". Aslında yöntemin javadocs'tan tam dokümantasyonu yayınladı ...
matt b

2
@Software Monkey: Evet, onu yeni düzenleyebilirdim. Ancak Dean J açıkça aktif olduğu için (sadece birkaç dakika önce yayınladığı için), ondan bunu yapmasını istemenin bir nezaket olduğunu düşündüm. Eğer yapmasaydı, buraya geri gelir, düzenlemeyi yapar ve yorumumu silerdim.
Daniel Pryden

1
Ayrıca NEDEN tavsiye edilmediğini de söylemeye değer. JVM, GC'yi çalıştırma "önerisine" dikkat ederse, neredeyse kesinlikle uygulamanızın daha yavaş çalışmasını sağlayacaktır, muhtemelen pek çok büyüklük sırası ile!
Stephen C

11

* "Ben şahsen değişkenleri ileride düzgün bir şekilde silmek için bir yer tutucu olarak geçersiz kılmaya güveniyorum. Örneğin, dizinin kendisini fiilen silmeden (boş bırakmadan) önce bir dizinin tüm öğelerini geçersiz kılmak için zaman ayırıyorum."

Bu gereksizdir. Java GC'nin çalışma şekli, kendilerine referansı olmayan nesneleri bulmasıdır, bu nedenle, referansa (= değişken) a işaret eden bir Nesne x'e sahipsem, GC onu silmez, çünkü bir referans vardır. o nesneye:

a -> x

Eğer a'yı boş bırakırsanız, bu olur:

a -> null
     x

Yani şimdi x'in kendisine işaret eden bir referansı yok ve silinecek. Aynı şey, a'yı x'ten farklı bir nesneye referans olarak ayarladığınızda da olur.

Dolayısıyla, x, y ve z nesnelerine başvuran bir dizi diziniz ve diziye başvuran bir a değişkenine sahipseniz, şöyle görünür:

a -> arr -> x
         -> y
         -> z

Eğer a'yı boş bırakırsanız, bu olur:

a -> null
     arr -> x
         -> y
         -> z

Böylece GC, arr'ı kendisine referans set edilmemiş olarak bulur ve siler, bu da size şu yapıyı verir:

a -> null
     x
     y
     z

Şimdi GC x, y ve z'yi bulur ve onları da siler. Dizideki her referansı sıfırlamak daha iyi bir şey yapmaz, koddaki CPU zamanını ve alanını kullanır (bu, bundan daha fazla zarar vermez. GC yine de olması gerektiği gibi çalışabilir. ).


5

Herhangi bir programdan (java ya da değil) bellek boşaltmayı istemenin geçerli bir nedeni, işletim sistemi düzeyinde diğer programlara daha fazla bellek sağlamaktır. Java uygulamam 250MB kullanıyorsa, onu 1MB’ye düşürmek ve 249 MB’ı diğer uygulamaların kullanımına açmak isteyebilirim.


Bir Java programında 249 MB'lık bir parçayı açıkça boşaltmanız gerekiyorsa, bellek yönetimi üzerinde çalışmak isteyeceğim ilk şey olmazdı.
Marc DiMillo

3
Ancak Java yığınınızın içindeki depolamayı boşaltmak (genel durumda), depolamayı diğer uygulamaların kullanımına sunmaz.
Hot Licks

5

Yiannis Xanthopoulos ve Hot Licks'in cevabını ve yorumunu genişletmek için (üzgünüm, henüz yorum yapamam!), Aşağıdaki örnek gibi VM seçeneklerini ayarlayabilirsiniz:

-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30

Benim jdk 7'mde, VM boştayken GC'den sonra yığının% 30'undan fazlası boş hale gelirse, kullanılmayan VM belleğini serbest bırakır. Muhtemelen bu parametreleri ayarlamanız gerekecek.

Aşağıdaki bağlantıda vurgulandığını görmemiş olsam da, bazı çöp toplayıcıların bu parametrelere uymayabileceğini ve birden fazla çekirdeğiniz olması durumunda varsayılan olarak java'nın sizin için bunlardan birini seçebileceğini unutmayın (bu nedenle yukarıdaki UseG1GC argümanı) ).

VM argümanları

Güncelleme: Java 1.8.0_73 için JVM'nin zaman zaman varsayılan ayarlarla küçük miktarlar yayınladığını gördüm. Görünüşe göre, bunu yalnızca yığının ~% 70'i kullanılmamışsa yapıyor .. İşletim sisteminin fiziksel belleğinde düşük olsaydı daha agresif bir sürüm olup olmayacağını bilmiyorum.


4

Bunun üzerinde deneyler yaptım.

System.gc();Yalnızca Çöp Toplayıcıyı çalıştırmayı önerdiği doğrudur .

Ancak , System.gc();tüm referansları ayarladıktan sonra çağırmak null, performansı ve hafıza kullanımını artıracaktır.


Sanırım "System.gc () 'yi çağırmanın; tüm referansları null olarak ayarladıktan sonra, performansı ve hafıza kullanımını artıracağını" kesin olarak söyleyemezsiniz. Çünkü System.gc () çok büyük bir hesaplama karmaşıklığı vardır. Ve System.gc () 'yi çağırdıktan ve aslında çöpü topladıktan sonra bile, jvm belleği İşletim Sistemine veya Sisteme geri vermeyebilir. JVM, ileride başvurmak üzere belleği saklayabilir. Bu cevaba bakın .
Md. Abu Nafee Ibna Zahid

3

Gerçekten bir bellek bloğu tahsis etmek ve boşaltmak istiyorsanız, bunu doğrudan ByteBuffers ile yapabilirsiniz. Belleği boşaltmanın taşınabilir olmayan bir yolu bile var.

Bununla birlikte, önerildiği gibi, sadece C'de hafızayı boşaltmanız gerektiği için, bunu yapmak zorunda olmanın iyi bir fikir olduğu anlamına gelmez.

Ücretsiz () için gerçekten iyi bir kullanım durumunuz olduğunu düşünüyorsanız, lütfen bunu soruya ekleyin, böylece ne yapmak istediğinizi görebiliriz, büyük olasılıkla daha iyi bir yol vardır.


3

Tamamen javacoffeebreak.com/faq/faq0012.html adresinden

Düşük öncelikli bir iş parçacığı, kullanıcı için otomatik olarak çöp toplamayı halleder. Boşta kalma süresi sırasında, iş parçacığı çağrılabilir ve önceden Java'da bir nesneye tahsis edilmiş belleği boşaltmaya başlayabilir. Ancak endişelenmeyin - üstünüzdeki nesneleri silmez!

Bir nesneye herhangi bir referans olmadığında, çöp toplayıcı için adil bir oyun haline gelir. Bazı rutinleri çağırmak yerine (C ++ 'da ücretsiz olduğu gibi), nesneye yapılan tüm başvuruları null olarak atarsınız veya referansa yeni bir sınıf atarsınız.

Misal :

public static void main(String args[])
{
  // Instantiate a large memory using class
  MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);

  // Do some work
  for ( .............. )
  {
      // Do some processing on myClass
  }

  // Clear reference to myClass
  myClass = null;

  // Continue processing, safe in the knowledge
  // that the garbage collector will reclaim myClass
}

Kodunuz büyük miktarda bellek talep etmek üzereyse, düşük öncelikli bir iş parçacığı olarak bunu yapmasına izin vermek yerine, çöp toplayıcının alanı geri kazanmaya başlamasını isteyebilirsiniz. Bunu yapmak için, kodunuza aşağıdakileri ekleyin

System.gc();

Çöp toplayıcı, boş alanı geri kazanmaya çalışır ve uygulamanız, mümkün olduğu kadar çok bellek geri kazanılmış olarak çalışmaya devam edebilir (bellek parçalanması sorunları belirli platformlarda geçerli olabilir).


1

Benim durumumda, Java kodumun yakın gelecekte diğer dillere taşınması gerektiği için (Temelde C ++), en azından belleği düzgün bir şekilde boşaltmak için sözde hizmet vermek istiyorum, böylece daha sonra taşıma işlemine yardımcı olur.

Ben şahsen, gelecekte uygun şekilde silme işlemi için değişkenleri bir yer tutucu olarak geçersiz kılmaya güveniyorum. Örneğin, dizinin kendisini fiilen silmeden (boş bırakmadan) önce bir dizinin tüm öğelerini geçersiz kılmak için zaman ayırıyorum.

Ama benim durumum çok özel ve bunu yaparken performans hitleri aldığımı biliyorum.


1

* "Örneğin, boyutu çok büyük olacak şekilde büyüyen, ancak yalnızca yöntemin yarısına kadar gerekli olan bir yöntemin başlangıcında bir Liste ilan ettiğinizi varsayalım. Bu noktada List referansını null olarak ayarlayabilirsiniz. yöntem tamamlanmadan önce çöp toplayıcının bu nesneyi potansiyel olarak geri almasına izin vermek için (ve referans yine de kapsam dışında kalır). " *

Bu doğru, ancak bu çözüm genelleştirilemez. Bir List nesnesi başvurusunu null -will- çöp toplama için kullanılabilir hale getirirken, bu yalnızca ilkel türlerin List nesnesi için geçerlidir. List nesnesi bunun yerine başvuru türleri içeriyorsa, List nesnesinin = null ayarlanması, listedeki -herhangi- başvuru türlerinin başvurusunu kaldırmaz. Bu durumda, List nesnesinin = null olarak ayarlanması, çöp toplama algoritması nesnelerin sahipsiz kaldığını belirleyecek kadar akıllı olmadığı sürece, nesneleri çöp toplama için kullanılamayacak olan içerilen referans türlerini yetim bırakacaktır.


1
Bu aslında doğru değil. Java çöp toplayıcı bunu doğru bir şekilde halledecek kadar akıllıdır. Listeyi boş bırakırsanız (ve Listedeki nesnelerin bunlara başka başvuruları yoksa), GC Liste içindeki tüm nesneleri geri alabilir. Şu anda bunu yapmamayı seçebilir, ancak sonunda onları geri alacaktır. Aynısı döngüsel referanslar için de geçerlidir. Temel olarak, GC'nin çalışma şekli, özellikle sahipsiz nesneleri aramak ve sonra onları geri kazanmaktır. Bu bir GC'nin tüm işi. Onu tarif etme şekliniz bir GC'yi tamamen işe yaramaz hale getirir.
Dakkaron

1

Althrough java otomatik çöp toplama bazen nesnenin ne kadar büyük bilmek ve ne kadar bunun programlama kullanılarak .Free bellek bırakılır isteyeceksiniz sağlar import java.lang;ve Runtime r=Runtime.getRuntime(); kullanan bellek değerlerini elde etmek için mem1=r.freeMemory();boş bellek çağrısına r.gc();yöntem ve çağrıfreeMemory()


1

JAVA'nın tavsiyesi boş atamaktır

Gönderen https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

Artık ihtiyaç duyulmayan değişkenlere açık bir şekilde boş değer atamak, çöp toplayıcının belleğin güvenli bir şekilde geri kazanılabilen bölümlerini tanımlamasına yardımcı olur. Java, bellek yönetimi sağlasa da, bellek sızıntılarını veya aşırı miktarda bellek kullanımını engellemez.

Bir uygulama, nesne referanslarını serbest bırakmayarak bellek sızıntılarına neden olabilir. Bunu yapmak, Java çöp toplayıcısının bu nesneleri geri kazanmasını engeller ve kullanılan bellek miktarının artmasıyla sonuçlanır. Kullanımlarından sonra değişkenlere yapılan başvuruları açıkça geçersiz kılmak, çöp toplayıcının belleği geri kazanmasına izin verir.

Bellek sızıntılarını tespit etmenin bir yolu, profil oluşturma araçlarını kullanmak ve her işlemden sonra bellek anlık görüntülerini almaktır. Kararlı durumda sızıntısız bir uygulama, çöp toplama işlemlerinden sonra sabit bir aktif yığın belleği gösterecektir.

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.