System.gc () ne zaman bir şey yapar?


118

Java'da çöp toplamanın otomatik olduğunu biliyorum. Ancak System.gc(), kodunuzu ararsanız , JVM'nin bu noktada çöp toplama yapmaya karar verebileceğini veya etmeyeceğini anladım . Bu tam olarak nasıl çalışıyor? JVM, gördüğü zaman bir GC yapmaya (veya yapmamaya) tam olarak hangi temelde / parametrelere dayanarak karar verir System.gc()?

Bunu kodunuza eklemenin iyi bir fikir olduğu herhangi bir örnek var mı?


4
Bir kavramın tüm ayrıntıları burada çok karmaşık, ancak bu makale size yardımcı olmalı: chaoticjava.com/posts/how-does-garbage-collection-work
GEOCHET

Kesin davranış JVM'ye bağlıdır, yani uygulamaya bağlıdır.
Thorbjørn Ravn Andersen

Yanıtlar:


59

Pratikte, genellikle çöp toplama yapmaya karar verir. Cevap, hangi JVM üzerinde çalıştığınız, hangi modda olduğu ve hangi çöp toplama algoritmasını kullandığı gibi birçok faktöre bağlı olarak değişir.

Senin koduna güvenmem. JVM bir OutOfMemoryError atmak üzereyse, System.gc () 'yi çağırmak onu durdurmayacaktır, çünkü çöp toplayıcı bu uç noktaya gelmeden önce olabildiğince serbest bırakmaya çalışacaktır. Pratikte kullanıldığını gördüğüm tek zaman IDE'lerde, bir kullanıcının tıklayabileceği bir düğmeye eklenmiş, ama orada bile çok kullanışlı değil.


3
jconsole'da çöp toplamaya zorlayabilirsiniz
Benedikt Waldvogel

25
@bene Gerçekten "zorlayabilir misin"? Jconsole sadece System.gc () 'yi mi çağırıyor?
John Meagher

4
Bana kalırsa doğru System.gc()bir video oyunu için bir yükleme ekranında süre. Bu noktada, görüşmenin mümkün olan her şeyi temizlemesi veya hiçbir şey yapmaması umurumda olmazdı. Bununla birlikte, oyun sırasında değil, yükleme ekranı sırasında "kullanılmayan nesneleri geri dönüştürmek için çaba harcamasını" tercih ederim .
Kröw

30

System.gc () 'yi çağırmanın mantıklı olduğu yeri düşünebildiğim tek örnek, olası bellek sızıntılarını aramak için bir uygulamanın profilini oluştururken. Profilcilerin bu yöntemi bellek anlık görüntüsü almadan hemen önce çağırdığına inanıyorum.


4
Evet, ben de öyle yapıyorum. Runtime.freeMemory () tarafından döndürülen değerler, örneğin bir System.gc () 'den sonra gerçekten anlamlıdır, çünkü normalde toplanamayan örnekler tarafından ne kadar bellek engellendiğini bilmek istersiniz. Sadece hata ayıklama / bellek profili oluşturmada kullanılmalı, asla üretimde kullanılmamalıdır.
sleske

Hayır, diğer birçok senaryo için de iyi. Örneğin, yaşam döngüsüne duyarlı tek bir kalıbınız olduğunu varsayalım. OnStop () 'da örneği boş bırakıyorsanız, ona yardım etmesi için System.gc ()' yi çağırmak iyi bir fikirdir.
portfoliobuilder

26

Java Dil Belirtimi, aradığınızda JVM'nin bir GC başlatacağını garanti etmez System.gc(). Bunun nedeni "bu noktada bir GC yapmaya karar verebilir veya etmeyebilir".

Şimdi, Oracle JVM'nin omurgası olan OpenJDK kaynak koduna bakarsanız, bir çağrının System.gc()bir GC döngüsü başlattığını göreceksiniz . J9 gibi başka bir JVM kullanıyorsanız, cevabı bulmak için belgelerine bakmanız gerekir. Örneğin, Azul'un JVM'sinde sürekli çalışan bir çöp toplayıcı vardır, bu nedenleSystem.gc() hiçbir şey yapmaz.

Başka bir yanıt, JConsole veya VisualVM'de bir GC başlatmayı belirtir. Temel olarak, bu araçlar uzaktan arama yapar System.gc().

Genellikle, uygulamanızın anlamını bozduğundan, kodunuzdan bir çöp toplama döngüsü başlatmak istemezsiniz. Uygulamanız bazı ticari işler yapar, JVM bellek yönetimiyle ilgilenir. Bu endişeleri ayrı tutmalısınız (uygulamanıza biraz bellek yönetimi yaptırmayın, işe odaklanın).

Ancak, bir çağrının System.gc()anlaşılabilir olabileceği birkaç durum vardır . Örneğin, mikro ölçütleri düşünün. Hiç kimse bir mikro ölçütün ortasında bir GC döngüsünün olmasını istemez. Bu nedenle, her ölçümün boş bir yığınla başladığından emin olmak için her ölçüm arasında bir GC döngüsü tetikleyebilirsiniz.


26

Java'da GC üzerinde kontrolünüz yoktur - VM karar verir. Bir dava karşısında koşmak hiç System.gc()bir ihtiyaç . Bir System.gc()çağrı basitçe VM'nin bir çöp toplama yapmasını ve aynı zamanda bir TAM çöp toplama (çok nesilli bir yığın içinde eski ve yeni nesiller) yapmasını ÖNERDİĞİ için, aslında gereğinden daha fazla cpu döngüsünün tüketilmesine neden olabilir .

Bazı durumlarda, ağır kaldırma meydana gelmeden önce uygulamanın birkaç dakika boşta kalacağını bildiğinizden, VM'ye ŞİMDİ tam bir koleksiyon yapmasını önermek mantıklı olabilir. Örneğin, uygulama başlangıcı sırasında bir çok geçici nesnenin başlatılmasından hemen sonra (yani, bir TON bilgiyi önbelleğe aldım ve bir dakika kadar fazla etkinlik almayacağımı biliyorum). Tutulmanın başlaması gibi bir IDE düşünün - başlatmak için çok şey var, bu yüzden belki de başlatmadan hemen sonra tam bir gc yapmak o noktada mantıklı geliyor.


17

Ararsan çok dikkatli olmalısın System.gc(). Onu çağırmak, uygulamanıza gereksiz performans sorunları ekleyebilir ve gerçekten bir koleksiyon gerçekleştirmesi garanti edilmez. Aslında System.gc()java argümanı aracılığıyla açık olarak devre dışı bırakmak mümkündür -XX:+DisableExplicitGC.

Çöp toplama hakkında daha ayrıntılı ayrıntılar için Java HotSpot Garbage Collection'da bulunan belgeleri okumanızı şiddetle tavsiye ederim .


Android uygulamam her zaman bana bir OutOfMemoryException atıyor. Bu nedenle, tüm istenmeyen başvuruları ve nesneleri temizlemek için sınıfımın onDestroy işlevinde System.gc () kullanmayı düşünüyordum. İyi bir yaklaşım mı
Sagar Devanga

2
@Sagar Devanga Açıkladığınız gibi gc'yi manuel olarak aramak pek yardımcı olmayacaktır. Bir OOM atmadan önce, JVM hafızayı boşaltmak için çöp toplama girişiminde bulunmuş olacaktır
Scrubbie

10

System.gc()VM tarafından uygulanır ve yaptığı şey uygulamaya özeldir. Uygulayıcı, örneğin geri dönebilir ve hiçbir şey yapmaz.

Ne zaman manuel tahsilat yapacağınıza gelince, bunu yapmak isteyebileceğiniz tek zaman, çok sayıda küçük koleksiyon içeren büyük bir koleksiyonu terk ettiğiniz zamandır - Map<String,<LinkedList>> örneğin - ve o zaman mükemmel vuruş yapmayı denemek ve orada, ama çoğunlukla endişelenmemelisin. Genel Kurul sizden daha iyi bilir - ne yazık ki - çoğu zaman.


8

Doğrudan bellek arabellekleri kullanırsanız, doğrudan bellek azalmış olsa bile JVM, GC'yi sizin için çalıştırmaz.

Arama yaparsanız ByteBuffer.allocateDirect()ve bir OutOfMemoryError alırsanız, bir GC'yi manuel olarak tetikledikten sonra bu aramanın iyi olduğunu görebilirsiniz.


System.gc () 'nin doğrudan tahsisleri topladığını, ancak JVM'nin normalde almadığını mı söylüyorsunuz (bir OOM atmadan önce). Çünkü bunun yanlış olduğunu düşünüyorum. Bir ayırmanın, açık bir GC'den sonra başarılı olmasının tek nedeni, ek süre ve bir sonlandırıcının serbest bırakıldığı (ve bazı doğrudan tahsis edilmiş nesneyi serbest bırakan) bir yığın içi nesnenin biraz daha olası olmasıdır.
Eckes

Java 6'nın en son sürümünde, assignateBuffer'daki kod, bir OutOfMemoryError atmadan önce her zaman bir System.gc () çalıştıracaktır. Sonlandırma kodunda, Cleanersdoğrudan belleğin kullandığı bir kısayol vardır . yani, çok yavaş olabileceğinden sonlandırıcı iş parçacığı tarafından serbest bırakılmaz. Yine de, özellikle hızlı bir şekilde doğrudan bellek bölgeleri oluşturuyorsanız, bir OOME elde etmek mümkündür. Çözüm, bunu yapmamak ve yapabildiğiniz yerde doğrudan bellek bölgelerinizi yeniden kullanmaktır.
Peter Lawrey

1
teşekkürler, bu daha çok benim deneyimime benziyor. Bu yüzden cevabın yalnızca eski Java sürümleri için doğru olduğunu tahmin ediyorum.
Eckes

5

Çoğu JVM bir GC başlatır (-XX: DiableExplicitGC ve -XX: + ExplicitGCInvokesConcurrent anahtarına bağlı olarak). Ancak daha sonra daha iyi uygulamalara izin vermek için spesifikasyon daha az iyi tanımlanmıştır.

Spesifikasyonun açıklığa kavuşturulması gerekiyor: Hata # 6668279: (spec) System.gc (), kullanımı önermediğimizi ve davranış garantisi vermediğimizi belirtmelidir

Dahili olarak gc yöntemi RMI ve NIO tarafından kullanılır ve senkronize yürütme gerektirirler, ki bu şu anda tartışılmaktadır:

Hata # 5025281: System.gc () 'nin eşzamanlı (dünyayı durdurmayan) tam koleksiyonları tetiklemesine izin ver


3

Garbage Collectioniyi olduğunu Java, biz Masaüstü / dizüstü / sunucudaki java kodlu Yazılımı yürütme eğer. Sen diyebilirsin System.gc()ya Runtime.getRuntime().gc()içinde Java.

Sadece bu aramaların hiçbirinin bir şey yapmayacağının garanti edilmediğini unutmayın. Bunlar jvm'nin Çöp Toplayıcıyı çalıştırması için sadece bir öneridir. GC'yi çalıştırıp çalıştırmayacağı JVM'nin üstünde. Yani, kısa cevap: ne zaman çalıştığını bilmiyoruz. Daha uzun cevap: JVM, bunun için zamanı varsa gc'yi çalıştırır.

Aynı şeyin Android için de geçerli olduğuna inanıyorum. Ancak bu, sisteminizi yavaşlatabilir.


JVM, bunun için zamanı varsa gc'yi çalıştırır : her zaman doğru değil, Jvm, Eden belleği dolduğunda gc'yi çalıştırabilir.
abdelmouheimen

2

Normalde, sanal makine bir OutOfMemoryException oluşturmadan önce otomatik olarak bir çöp toplama işlemi yapar, bu nedenle açık bir çağrı eklemek, performans vuruşunu zamandaki daha erken bir ana götürmesi dışında yardımcı olmamalıdır.

Ancak, ilgili olabileceği bir durumla karşılaştığımı düşünüyorum. Yine de emin değilim, çünkü henüz herhangi bir etkisi olup olmadığını test etmedim:

Bir dosyanın belleğini eşlediğinizde, yeterince büyük bir bellek bloğu mevcut olmadığında map () çağrısının bir IOException attığına inanıyorum. Map () dosyasından hemen önceki bir çöp toplama işlemi bunu önlemeye yardımcı olabilir, sanırım. Ne düşünüyorsun?


2

çöp toplamayı asla zorlayamayız. System.gc sadece çöp toplama için vm önermektedir, ancak mekanizmanın ne zaman çalıştığını gerçekten kimse bilmiyor, bu JSR belirtimlerinde belirtildiği gibi.


2

Çeşitli çöp toplama ayarlarını test etmek için zaman ayırırken söylenecek çok şey vardır, ancak yukarıda belirtildiği gibi genellikle bunu yapmak yararlı değildir.

Şu anda bellek sınırlı bir ortamı ve nispeten büyük miktarda veriyi içeren bir proje üzerinde çalışıyorum - ortamımı sınırlarına kadar zorlayan birkaç büyük veri parçası var ve bellek kullanımını azaltabilmiş olsam da teoride gayet iyi çalışması gerektiğini, yine de yığın alanı hataları alacağımı --- ayrıntılı GC seçenekleri bana çöp toplamaya çalıştığını gösterdi, ancak işe yaramadı. Hata ayıklayıcıda, System.gc () işlemini gerçekleştirebilirim ve "bol miktarda" kullanılabilir bellek olduğundan emin olabilirim ... çok fazla değil, ancak yeterli.

Sonuç olarak, uygulamamın System.gc () 'yi çağırdığı tek zaman, verilerin işlenmesi için gerekli büyük tamponların tahsis edileceği kod segmentine girmek üzere olduğu zamandır ve mevcut boş hafıza üzerinde yapılan bir test benim olmadığımı gösterir. sahip olmak garantilidir. Özellikle, en az 300 MB'nin statik veriler tarafından işgal edildiği, işlenen verilerin en az 100-200 MB olduğu durumlar dışında, statik olmayan verilerin büyük bir kısmının yürütmeyle ilgili olduğu bir 1gb ortamına bakıyorum. kaynak. Bunların hepsi otomatik veri dönüştürme sürecinin bir parçasıdır, bu nedenle verilerin tümü uzun vadede nispeten kısa süreler için mevcuttur.

Ne yazık ki, çöp toplayıcının ayarlanması için çeşitli seçenekler hakkında bilgi mevcut olsa da, büyük ölçüde deneysel bir süreç gibi görünmektedir ve bu özel durumların nasıl ele alınacağını anlamak için gereken alt düzey özellikler kolayca elde edilemez.

Tüm söylenenler, System.gc () kullanmama rağmen, komut satırı parametrelerini kullanarak ayarlamaya devam ettim ve uygulamamın genel işlem süresini, üstesinden gelemememe rağmen nispeten önemli bir miktarda iyileştirmeyi başardım. Daha büyük veri bloklarıyla çalışarak ortaya çıkan tökezleyen blok. Bununla birlikte, System.gc () bir araçtır .... çok güvenilmez bir araçtır ve onu nasıl kullandığınıza dikkat etmezseniz, daha sık çalışmamasını dileyeceksiniz.


2

System.gc()Aramanızın aranıp aranmadığını bilmek istiyorsanız , yeni Java 7 güncelleme 4 ile JVM Çöp Toplama işlemi gerçekleştirdiğinde bildirim alabilirsiniz.

GarbageCollectorMXBean sınıfının Java 7 güncelleme 4'te tanıtıldığından % 100 emin değilim , çünkü sürüm notlarında bulamadım, ancak bilgileri javaperformancetuning .com sitesinde buldum


0

Açık GC çalıştırmanın iyi olduğu durumlarda belirli bir örnek düşünemiyorum.

Genel olarak, açık GC çalıştırmak aslında yarardan çok zarara neden olabilir, çünkü açık bir gc tam bir koleksiyonu tetikleyecektir ve bu, her nesneden geçerken önemli ölçüde daha uzun sürer. Bu açık gc tekrar tekrar çağrılırsa, tam GC'leri çalıştırmak için çok zaman harcandığından kolayca yavaş bir uygulamaya yol açabilir.

Alternatif olarak, bir yığın çözümleyiciyle öbek üzerinden gidiyorsanız ve bir kitaplık bileşeninin açık GC'leri çağırdığından şüpheleniyorsanız, JVM parametrelerine gc = -XX: + DisableExplicitGC ekleyerek kapatabilirsiniz.


2
Nesneler üzerinde 'close ()' olarak adlandırmamıza rağmen, aksi takdirde kapalı olmayan ve bu nedenle diğer işlemler için veya dosya kesme için kullanılamayan MappedByteBuffer nesnelerimizi kapatmaya çalışmak için System.gc () 'yi çağırıyoruz. Maalesef yine de onları her zaman kapatmıyor.
Tim Cooper

0

Bruce Eckel tarafından Java'da Düşünme konusuna göre, açık System.gc () çağrısı için bir kullanım durumu, sonlandırmayı zorlamak istediğiniz zamandır, yani sonlandırma yöntemi çağrısı .


Not: Sonlandırma çalışmasını zorlamak için kendi yöntemi vardır. (Sorun aslında sonuçlandırıcıları çalıştırmak değil, başvurulmadığı için bir nesnenin sonuçlandırılabileceğini kabul etmektir)
2012'de 1

-1

system.gc çalışırken dünyayı durdurur: tüm yanıtlar durdurulur, böylece çöp toplayıcı silinmesi gerekip gerekmediğini kontrol etmek için her nesneyi tarayabilir. uygulama bir web projesi ise, gc bitene kadar tüm istekler durdurulur ve bu, web projenizin bir monentte çalışmamasına neden olur.

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.