System.gc () öğesini çağırmak neden kötü bir uygulamadır?


326

Java ile nesnelerin nasıl zorlanacağı ile ilgili bir soruya cevap verdikten sonra (adam 1,5GB'lık bir HashMap temizliyordu) , manuel olarak aramanın kötü bir uygulama olduğu söylendi , ancak yorumlar tamamen ikna edici değildi. Buna ek olarak, hiç kimse benim cevabımı ya da cevabımı aşağılamaya cesaret edemedi.System.gc()System.gc()

Bana bunun kötü bir uygulama olduğu söylendi, ama sonra da çöp toplayıcı çalışmalarının artık sistematik olarak dünyayı durdurmadığı ve JVM tarafından sadece bir ipucu olarak etkili bir şekilde kullanılabileceği söylendi. Kayıp.

JVM'nin hafızayı geri kazanması gerektiğinde genellikle sizden daha iyi bildiğini anlıyorum. Birkaç kilobaytlık veri için endişelenmenin saçma olduğunu da anlıyorum. Ayrıca, megabaytlarca verinin bile birkaç yıl öncesine ait olmadığını anlıyorum. Ama yine de 1,5 gigabayt? Ve biliyorsunuz , bellekte takılan 1,5 GB gibi bir veri var; karanlıkta bir atış gibi değil. System.gc()Sistematik olarak kötü mü , yoksa iyi olduğu bir nokta var mı?

Yani soru aslında iki katına çıkıyor:

  • Neden aramak kötü bir uygulama System.gc()? Gerçekten belirli uygulamalar altında JVM'ye bir ipucu mu yoksa her zaman tam bir toplama döngüsü mü? Dünyayı durdurmadan işlerini yapabilen gerçekten çöp toplayıcı uygulamaları var mı? Lütfen insanların cevabımın yorumlarında yaptığı çeşitli iddialara ışık tutunuz .
  • Eşik nerede? Aramak asla iyi bir fikir değil mi System.gc(), ya da kabul edilebilir olduğu zamanlar var mı? Eğer öyleyse, o zamanlar ne?

4
Zaten uzun bir yükleme işlemi olan bir şey yaptığınızda System.gc () çağırmak için iyi bir zaman olduğunu düşünüyorum. Örneğin, bir oyun üzerinde çalışıyorum ve oyun yeni bir seviye yüklediğinde, yükleme işleminin sonunda System.gc () 'yi çağırmayı planlıyorum. Kullanıcı zaten biraz bekliyor ve ekstra performans artışı buna değebilir; ancak bu davranışı devre dışı bırakmak için yapılandırma ekranına bir seçenek de ekleyeceğim.
Ricket

41
Bozho: sorunun ilk kelimesini not edin. Niçin dememek en iyi uygulamadır? Bir mantrayı tekrarlamak lanet olası bir şeyi açıklamaz.
SADECE benim doğru görüşüm

1
Başka bir vaka java-monitor.com/forum/showthread.php?t=188 adresinde açıklanmıştır. Burada System.gc () olarak adlandırmanın nasıl kötü bir uygulama olabileceğini açıklar
Shirishkumar Bari

Yanıtlar:


243

Nedeni herkes her zaman kaçınmak için diyor System.gc()bir olmasıdır temelden kırık kod oldukça iyi göstergesi . Doğruluk için ona bağlı olan herhangi bir kod kesinlikle bozulur; performans için ona güvenen her şey büyük olasılıkla bozulur.

Ne tür çöp toplayıcının altında koştuğunuzu bilmiyorsunuz. İddia ettiğiniz gibi kesinlikle "dünyayı durdurmayan" bazıları vardır, ancak bazı JVM'ler o kadar akıllı değildir veya çeşitli nedenlerle (belki de bir telefonda mıdırlar) bunu yapmazlar. Ne yapacağını bilmiyorsun.

Ayrıca, hiçbir şey yapması garanti edilmez. JVM isteğinizi tamamen göz ardı edebilir.

"Ne yapacağını bilmiyorsun", "yardım edip edemeyeceğini bile bilmiyorsun" ve "yine de çağırmana gerek yok" kombinasyonu insanların genel olarak onu aramamalısın. Bence bu bir "bunu kullanmanız gerekip gerekmediğini sormanız gerekiyorsa yapmamalısınız"


Diğer iş parçacığından birkaç endişeyi gidermek için DÜZENLE :

Bağladığınız konuyu okuduktan sonra, belirtmek istediğim birkaç şey daha var. İlk olarak, birisi aramanın gc()belleği sisteme geri verebileceğini önerdi . Bu kesinlikle doğru değildir - Java öbeğinin kendisi Java tahsislerinden bağımsız olarak büyür.

JVM, olduğu gibi hafızayı (onlarca megabayt) tutacak ve gerektiğinde yığını artıracaktır. Java nesnelerini serbest bıraktığınızda bile bu belleği sisteme döndürmesi gerekmez; gelecekteki Java ayırmaları için kullanmak üzere ayrılan belleğe tutunmak tamamen ücretsizdir.

System.gc()Hiçbir şey yapmamanın mümkün olduğunu göstermek için şunları görüntüleyin:

http://bugs.sun.com/view_bug.do?bug_id=6668279

özellikle de -XX: DisableExplicitGC VM seçeneği var.


1
GC'nin çalıştırıldığı yöntemin kodunuzun doğruluğunu etkilediği bazı garip Rube Goldberg-esque kurulumları yapabilirsiniz. Belki de garip bir diş çekme etkileşimini maskeliyor veya belki bir finalizörün programın çalışması üzerinde önemli bir etkisi var. Bunun mümkün olduğundan tam olarak emin değilim ama olabilir, bu yüzden bahsettiğimi düşündüm.
Steven Schlansker

2
@zneak, finalizörlere kritik kod koyabilirsiniz (temelde kırık koddur)
Martin

3
System.gc()Faydalı ve hatta gerekli olabilecek birkaç köşe vakası olduğunu eklemek isterim . Örneğin, Windows üzerindeki UI uygulamalarında, Pencereyi simge durumuna küçültmeden önce System.gc () öğesini çağırdığınızda bir Pencerenin geri yükleme işlemini büyük ölçüde hızlandırabilir (özellikle bir süre simge durumuna küçültülmüş olarak kalır ve işlemin bazı bölümleri değiştirilirse) disk).
Joachim Sauer

2
@AndrewJanke Tutmak istediğiniz WeakReferencenesneler için s kullanan kodun başlangıç, çöp toplama veya yanlış olduğunu söyleyebilirim . C ++ ile aynı sorunu yaşarsınız std::weak_ptr(yine de bir C ++ sürümünde sorunu bir Java sürümünde olduğundan daha önce fark edebilirsiniz, çünkü nesne imhası genellikle sonlandırma gibi ertelenmez).
JAB

2
@rebeccah Bu bir hata, bu yüzden evet, 'kesinlikle kırılmış' diyeceğim. Bunu System.gc()düzelten bir çözüm, iyi bir kodlama uygulaması değil.
Steven Schlansker

149

Zaten bu çağrı açıkladı olmuştur system.gc() edebilir hiçbir şey ve herhangi kod "ihtiyaçlar" kaçak çöp toplayıcı kırık olduğunu söyledi.

Bununla birlikte, çağıran kötü uygulamanın pragmatik nedeni, bunun System.gc()verimsiz olmasıdır. Ve en kötü durumda, korkunç derecede verimsiz ! Açıklamama izin ver.

Tipik bir GC algoritması, çöp yığınındaki tüm çöp olmayan nesneleri dolaşarak ve ziyaret edilmeyen herhangi bir nesnenin çöp olması gerektiğinden çıkarım yaparak çöpü tanımlar. Bundan, bir çöp toplama toplam çalışmasının, canlı veri miktarı ile orantılı bir kısımdan ve çöp miktarı ile orantılı başka bir kısımdan oluşmasını modelleyebiliriz; yani work = (live * W1 + garbage * W2).

Şimdi, aşağıdakileri tek iş parçacıklı bir uygulamada yaptığınızı varsayalım.

System.gc(); System.gc();

İlk çağrı (tahmin edeceğiz) işe (live * W1 + garbage * W2)yarayacak ve olağanüstü çöplerden kurtulacak.

İkinci çağrı işe (live* W1 + 0 * W2)yarayacak ve hiçbir şey talep etmeyecektir . Başka bir deyişle, (live * W1)iş yaptık ve kesinlikle hiçbir şey başaramadık .

Toplayıcının verimliliğini, bir birim çöp toplamak için gereken iş miktarı olarak modelleyebiliriz; yani efficiency = (live * W1 + garbage * W2) / garbage. Dolayısıyla, GC'yi mümkün olduğunca verimli hale getirmek için, GC'yi çalıştırdığımız zaman değerini en üst düzeyegarbage çıkarmamız gerekir; yani yığın dolana kadar bekleyin. (Ve ayrıca, yığını olabildiğince büyüt. Ama bu ayrı bir konu.)

Uygulama müdahale etmezse (arayarak System.gc()), GC, çalışmadan önce yığın doluncaya kadar bekler, böylece verimli çöp 1 toplanır . Ancak uygulama GC'yi çalışmaya zorlarsa, öbek dolu olmayacak ve sonuçta çöpün verimsiz bir şekilde toplanması olasılığı ortaya çıkacaktır. Ve uygulama GC'yi ne kadar sık ​​zorlarsa, GC o kadar verimsiz hale gelir.

Not: Yukarıdaki açıklama, tipik bir modern GC'nin yığını "boşluklara" böldüğü gerçeğine göre, GC, yığını dinamik olarak genişletebilir, uygulamanın çalışmayan çöp olmayan nesneler kümesi değişebilir vb. Buna rağmen, aynı temel prensip tüm gerçek çöp toplayıcılarına 2 uygulanır . GC'yi çalışmaya zorlamak verimsizdir.


1 - "Verim" toplayıcısı bu şekilde çalışır. CMS ve G1 gibi eşzamanlı toplayıcılar, çöp toplayıcının ne zaman başlatılacağına karar vermek için farklı ölçütler kullanır.

2 - Ayrıca, yalnızca referans sayma kullanan bellek yöneticilerini de hariç tutuyorum, ancak şu anda hiçbir Java uygulaması bu yaklaşımı kullanmıyor ... iyi bir nedenle.


45
+1 İyi açıklama. Ancak bu akıl yürütmenin yalnızca iş hacmini önemsediğinizde geçerli olduğunu unutmayın. Gizli noktaları belirli noktalarda optimize etmek istiyorsanız, GC'yi zorlamak mantıklı olabilir. Örneğin, bir oyunda (varsayımsal olarak konuşursak), seviyelerdeki gecikmeleri önlemek isteyebilirsiniz, ancak seviye yükü sırasındaki gecikmeleri umursamazsınız. O zaman seviye yükünden sonra GC'yi zorlamak mantıklı olacaktır. Genel verimi azaltır, ancak optimize ettiğiniz şey bu değildir.
sleske

2
@sleske - söyledikleriniz doğru. Bununla birlikte, GC'nin seviyeler arasında çalıştırılması bir bant yardımı çözümüdür ... ve eğer seviyeler GC'yi bir seviye sırasında çalıştırmanız için yeterince uzun sürerse gecikme problemini çözmez. Daha iyi bir yaklaşım, platform destekliyorsa, eşzamanlı (düşük duraklama) bir çöp toplayıcı kullanmaktır.
Stephen C

"Çalışmak için çöp toplayıcıya ihtiyacı var" ile ne demek istediğinizden tam olarak emin değilim. Uygulamaların kaçınması gereken şartlar; asla tatmin edilemeyen tahsis hataları ve yüksek GC yükü. System.gc () işlevine rastgele çağrı yapmak genellikle yüksek GC ek yükü ile sonuçlanır.
Kirk

@Kirk - "System.gc () işlevine rasgele çağrı yapmak genellikle yüksek GC ek yükü ile sonuçlanır." . Bunu biliyorum. Cevabımı okuyan ve anlayan herkes de öyle.
Stephen C

@Kirk - "Ne demek istediğinizi tam olarak bilmiyorum ..." - Bir programın "doğru" davranışının belirli bir zamanda çalışan GC'ye bağlı olduğu programları kastediyorum; Örneğin, sonlandırıcıları çalıştırmak veya WeakReferences veya SoftReferences'ı kırmak için. Bunu yapmak gerçekten kötü bir fikir ... ama bundan bahsediyorum. Ayrıca bkz. Steven Schlansker'ın yanıtı.
Stephen C

47

Birçok insan size bunu yapmamanızı söylüyor gibi görünüyor. Katılmıyorum. Seviye yüklemek gibi büyük bir yükleme işleminden sonra aşağıdakilere inanıyorsanız:

  1. Ulaşılamayan ve onaylanmamış birçok nesneniz var. ve
  2. Kullanıcının bu noktada küçük bir yavaşlamaya katlanabileceğini düşünüyorsunuz

System.gc () çağrılmasının bir zararı yoktur. Ben c / c ++ inlineanahtar kelime gibi bakmak . Geliştiricinin, zaman / performansın genellikle olduğu kadar önemli olmadığına ve bir kısmının belleği geri kazanmak için kullanılabileceğine karar verdiğiniz gc'ye bir ipucu.

Hiçbir şeye güvenmemeye dikkat etmek doğrudur. Çalışmasına güvenmeyin, ancak şimdi toplamak için kabul edilebilir bir zaman olduğuna dair ipucu vermek gayet iyi. Kodun, kullanıcının programla aktif olarak etkileşime girmesinden (oyunun bir seviyesi sırasında olduğu gibi) daha önemli olmadığı bir noktada (yükleme ekranı) zaman kaybetmeyi tercih ederim.

Ben toplama zorlamak için bir kez vardır : belirli bir nesne sızıntıları bulmaya çalışırken (yerel kod veya büyük, karmaşık geri arama etkileşimi. Oh ve Matlab bakışları kadar herhangi bir UI bileşeni.) Bu asla kullanılmamalıdır üretim kodunda.


3
Mem sızıntılarını analiz ederken GC için +1. Öbek kullanımı hakkındaki bilgilerin (Runtime.freeMemory () ve ark.) Gerçekten sadece bir GC zorladıktan sonra anlamlı olduğunu, aksi takdirde sistemin bir GC çalıştırmak için en son ne zaman uğraştığına bağlı olacağını unutmayın.
sleske

4
System.gc ()stop the world
yöntemini çağırmanın

7
Orada olduğunu açıkça çöp toplayıcı arayarak zararı. GC'yi yanlış zamanda çağırmak CPU döngülerini boşa harcar. Siz (programcı) doğru zamanın ne olduğunu belirlemek için yeterli bilgiye sahip değilsiniz ... ancak JVM bunu yapar.
Stephen C

Jetty başlatmadan sonra System.gc () için 2 çağrı yapar ve nadiren herhangi bir fark yarattığını gördüm.
Kirk

İskele geliştiricilerinin bir hatası var. Bir fark yaratır ... farkın ölçülmesi zor olsa bile.
Stephen C

31

İnsanlar neden KULLANMAYI açıklayan iyi bir iş çıkarıyorlar, bu yüzden size kullanmanız gereken birkaç durumu anlatacağım:

(Aşağıdaki yorumlar, Linux'ta CMS toplayıcı ile çalışan Hotspot için geçerlidir, burada System.gc()aslında her zaman tam bir çöp toplama çağrısında bulunduğunu söyleyebilirim ).

  1. Uygulamanızı ilk başlatmanızdan sonra, bellek kullanımında korkunç bir durum olabilir. Kiralanmış neslin yarısı çöple dolu olabilir, yani ilk CMS'nize çok daha yakınsınız demektir. Bunun önemli olduğu uygulamalarda, yığınınızı canlı verilerin başlangıç ​​durumuna "sıfırlamak" için System.gc () öğesini çağırmak kötü bir fikir değildir.

  2. # 1 ile aynı satırlar boyunca, yığın kullanımınızı yakından izlerseniz, temel bellek kullanımınızın ne olduğunu doğru bir şekilde okumak istersiniz. Uygulamanızın çalışma süresinin ilk 2 dakikasının tamamı başlatılırsa, tam gc'yi ön tarafa zorlamadığınız sürece (ahem ... "öneri") verileriniz dağıtılır.

  3. Çalışır durumdayken hiçbir zaman dayanıklı nesile hiçbir şeyi tanıtmayacak şekilde tasarlanmış bir uygulamanız olabilir. Ama belki de otomatik olarak kalıcı nesile taşınacak kadar büyük olmayan bazı verileri başlatmanız gerekir. Her şey ayarlandıktan sonra System.gc () yöntemini çağırmazsanız, verileriniz tanıtılma zamanı gelene kadar yeni nesilde oturabilir. Aniden süper duper düşük gecikmeli, düşük GC uygulamanız, normal işlemler sırasında bu nesneleri tanıtmak için BÜYÜK (göreceli olarak konuşan) bir gecikme cezasına çarptırılır.

  4. Bellek sızıntısının varlığını doğrulamak için üretim uygulamasında bir System.gc çağrısının bulunması bazen yararlı olabilir. X zamanındaki canlı veri kümesinin Y zamanındaki canlı veri kümesine belirli bir oranda olması gerektiğini biliyorsanız, System.gc () 'yi X ve Y zamanına çağırıp bellek kullanımını karşılaştırmak yararlı olabilir. .


1
Çoğu nesil çöp toplayıcı için, yeni nesildeki nesnelerin belirli sayıda (genellikle yapılandırılabilir) çöp koleksiyonundan hayatta kalması gerekir, bu nedenle System.gc()nesnelerin tanıtımını zorlamak için bir kez çağrı yapmak size hiçbir şey kazandırmaz. Ve kesinlikle System.gc()arka arkaya sekiz kez aramak ve dua etmek istemiyorsunuz , şimdi promosyon yapıldı ve daha sonraki bir promosyonun tasarruf maliyetleri birden fazla tam GC'nin maliyetini haklı çıkarıyor. GC algoritmasına bağlı olarak, çok sayıda nesneyi tanıtmak gerçek maliyeti bile taşımayabilir, çünkü belleği eski nesile yeniden atayacaktır veya eşzamanlı olarak kopyalayacaktır…
Holger

11

Bu çok rahatsız edici bir soru ve bir dilin ne kadar yararlı olduğuna rağmen birçoğunun Java'ya karşı çıkmasına katkıda bulunduğunu hissediyorum.

Bir şey yapmak için "System.gc" ye güvenemeyeceğiniz gerçeği inanılmaz göz korkutucu ve kolayca "Korku, Belirsizlik, Şüphe" hissini dile getirebilir.

Birçok durumda, kullanıcıların önemli bir şekilde tasarlandığını / yanıt vermediğini düşünmelerine neden olacak önemli bir olay gerçekleşmeden önce bilerek neden olduğunuz bellek artışlarıyla uğraşmak güzeldir.

Çöp koleksiyonunu kontrol etme yeteneğine sahip olmak çok iyi bir eğitim aracı olacaktır, bu da insanların çöp toplamanın nasıl çalıştığını ve programların varsayılan davranışını ve kontrollü davranışı nasıl kullanmasını sağladığını anlamalarını geliştirecektir.

Bu konunun argümanlarını gözden geçireyim.

  1. Verimsiz:

Genellikle, program hiçbir şey yapmıyor olabilir ve tasarlandığından dolayı hiçbir şey yapmadığını biliyorsunuz. Örneğin, büyük bir bekleme mesaj kutusu ile bir tür uzun bekleme yapıyor olabilir ve sonunda çöp toplamak için bir çağrı da ekleyebilir, çünkü çalıştırma süresi, zamanın çok küçük bir kısmını alacaktır. uzun süre beklemek ama gc'nin daha önemli bir operasyonun ortasında hareket etmesini önleyecektir.

  1. Her zaman kötü bir uygulamadır ve bozuk kodu gösterir.

Katılıyorum, ne çöp toplayıcıya sahip olduğunuz önemli değil. Görevi çöpleri temizlemek ve temizlemek.

Kullanımın daha az kritik olduğu zamanlarda gc'yi çağırarak, hayatınız çalıştırılan koda bağlı olduğunda çalışma olasılığını azaltırsınız, bunun yerine çöp toplamaya karar verir.

Elbette, istediğiniz gibi davranmayabilir veya beklemeyebilir, ancak aramak istediğinizde hiçbir şeyin olmadığını bilirsiniz ve kullanıcı yavaşlığı / kesinti süresini tolere etmeye istekli olur. System.gc çalışıyorsa, harika! Değilse, en azından denedin. Çöp toplayıcının, bir çöp toplayıcının manuel olarak çağrıldığında nasıl davranacağına dair beklenmedik bir şey yapan doğal yan etkileri yoksa ve bu tek başına güvensizliğe neden olmadıkça basit bir aşağı tarafı yoktur.

  1. Yaygın bir kullanım durumu değildir:

Güvenilir bir şekilde elde edilemeyen bir kullanım durumudur, ancak sistem bu şekilde tasarlanmışsa olabilir. Bu bir trafik ışığı yapmak ve trafik ışıklarının bazı / tüm düğmelerinin hiçbir şey yapmaması için yapmaktır, düğmenin neden orada olduğunu sorgulamanızı sağlar, javascript'in çöp toplama işlevi yoktur, bu yüzden onun için o kadar dikkatli olmayın.

  1. Spesifikasyon, System.gc () öğesinin GC'nin çalıştırması gereken bir ipucu olduğunu ve VM'nin bunu göz ardı etmekte özgür olduğunu söylüyor.

"ipucu" nedir? "yoksay" nedir? bir bilgisayar sadece ipuçlarını alamaz veya bir şeyi göz ardı edemez, sistemin amacı tarafından yönlendirilen dinamik olabilen katı davranış yolları vardır. Uygun bir cevap, çöp toplayıcının gerçekte ne yaptığını, uygulama düzeyinde, talep ettiğinizde toplama yapmamasına neden olur. Özellik basitçe bir nop mu? Karşılaşmam gereken bir tür koşul var mı? Bu koşullar nelerdir?

Java'nın GC'si genellikle güvenmediğiniz bir canavar gibi görünüyor. Ne zaman geleceğini veya gideceğini bilmiyorsun, ne yapacağını, nasıl yapacağını bilmiyorsun. Çöp Toplamalarının talimat başına nasıl çalıştığına dair daha iyi bir fikre sahip bazı hayal edebiliyorum, ancak büyük çoğunluğu sadece "sadece işe yaradığını" umuyor ve sizin için iş yapmak için opak görünen bir algoritmaya güvenmek sinir bozucu.

Bir şey hakkında okumak ya da bir şey öğretmek ve aslında onun uygulanmasını, sistemler arasındaki farklılıkları görmek ve kaynak koduna bakmak zorunda kalmadan onunla oynayabilmek arasında büyük bir boşluk vardır. Bu, güven ve ustalık / anlayış / kontrol hissi yaratır.

Özetlemek gerekirse, cevapların doğasında var olan bir sorun var "bu özellik hiçbir şey yapmayabilir ve ne zaman bir şey yaptığını ve ne zaman yapmadığını ve neden yapmayacağını veya yapmayacağını, çoğu zaman, arkasındaki niyet makul olsa bile, bunu yapmaya çalışmanın sadece felsefeye aykırı olduğunu ima eder ".

Java GC'nin yaptığı gibi davranması uygun olabilir veya olmayabilir, ancak anlamak için, GC'nin yapmasına güvenebileceğinize dair kapsamlı bir genel bakış elde etmek için hangi yöne gideceğinizi gerçekten takip etmek zordur ve yapmamak, bu yüzden dile güvenmemek çok kolaydır, çünkü bir dilin amacı felsefi ölçüye kadar kontrollü bir davranışa sahip olmaktır (bir programcının, özellikle acemilerin belirli sistem / dil davranışlarından varoluşsal krize girmesi kolaydır) tahammül edebilir (ve eğer yapamazsan, gerekene kadar dili kullanmayacaksın) ve onları kontrol edemediğin bilinen bir sebep olmadan kontrol edemediğin daha fazla şey doğal olarak zararlıdır.


9

GC verimliliği bir takım buluşsal yöntemlere dayanır. Örneğin, ortak bir sezgisel tarama, nesnelere yazma erişiminin genellikle uzun zaman önce yaratılmamış nesnelerde gerçekleşmesidir. Bir diğeri, birçok nesnenin çok kısa ömürlü olması (bazı nesneler uzun süre kullanılacak, ancak birçoğu oluşturulduktan sonra birkaç mikrosaniye atılacaktır).

Aramak System.gc()GC'yi tekmelemek gibidir. Bu şu anlama gelir: "dikkatle ayarlanmış tüm parametreler, bu akıllı organizasyonlar, nesnelerin tahsis edilmesi ve yönetilmesi için harcadığınız tüm çabalar, işler sorunsuz geçecek, iyi, sadece her şeyi bırakacak ve sıfırdan başlayacak". Bu olabilir performansını artırmak, ama sadece çoğu zaman alçaltır performansı.

System.gc()Güvenilir bir şekilde kullanmak için (*) GC'nin tüm ince detaylarında nasıl çalıştığını bilmeniz gerekir. Başka bir satıcıdan bir JVM veya aynı satıcıdan veya aynı JVM'den bir sonraki sürümü kullanıyorsanız ancak biraz farklı komut satırı seçenekleriyle bu tür ayrıntılar biraz değişme eğilimindedir. Bu nedenle, tüm bu parametreleri kontrol ettiğiniz belirli bir sorunu ele almak istemiyorsanız, nadiren iyi bir fikirdir. Bu nedenle "kötü uygulama" kavramı: bu yasak değildir, yöntem vardır, ancak nadiren işe yarar.

(*) Burada verimlilikten bahsediyorum. System.gc()asla kırmak doğru bir Java programı. Ayrıca, JVM'nin başka türlü elde edemeyeceği fazladan bir bellek yaratmayacaktır: bir atmadan önce OutOfMemoryErrorJVM, System.gc()son çare olarak bile işini yapar .


1
+1, System.gc () işlevinin OutOfMemoryError öğesini engellemediğini belirtmek için. Bazı insanlar buna inanır.
sleske

1
Aslında olabilir , çünkü yumuşak referansların elleçleme OutOfMemoryError önler. Son GC çalışmasından sonra oluşturulan SoftReferences biliyorum uygulamada toplanmaz. Ancak bu uygulama detayı her zaman değişebilir ve bir tür hata ve güvenmeniz gereken hiçbir şey yoktur.
maaartinus

8

Bazen ( sık sık değil! ) Geçmiş, şimdiki ve gelecekteki bellek kullanımı hakkında daha fazla bilgi sahibi olursunuz. Bu çok sık olmaz ve normal sayfalar sunulurken hiçbir zaman bir web uygulamasında talep edemezdim.

Yıllar önce bir rapor oluşturucu üzerinde çalışıyorum.

  • Tek bir iplik vardı
  • Bir kuyruktan "rapor isteği" ni okuyun
  • Rapor için gerekli verileri veritabanından yükledi
  • Raporu oluşturdu ve e-postayla gönderdi.
  • Olağanüstü hiçbir istek olmadığında uyku sonsuza kadar tekrarladı.
  • Raporlar arasında herhangi bir veriyi yeniden kullanmadı ve nakit para yatırma yapmadı.

Birincisi, gerçek zamanlı olmadığı ve kullanıcıların bir rapor beklemesi beklendiği için, GC çalışması sırasında bir gecikme bir sorun değildi, ancak raporları talep edilenden daha hızlı bir oranda üretmemiz gerekiyordu.

Sürecin yukarıdaki taslağına bakıldığında, açıktır.

  • Bir rapor gönderildikten hemen sonra çok az canlı nesne olacağını biliyoruz, çünkü bir sonraki istek henüz işlenmeye başlamamıştı.
  • Bir çöp toplama döngüsünün çalıştırılmasının maliyetinin, canlı nesnelerin sayısına bağlı olduğu , çöp miktarının bir GC çalışmasının maliyeti üzerinde çok az etkisi olduğu iyi bilinmektedir .
  • Sıra boş olduğunda, GC'yi çalıştırmak için daha iyi bir şey yoktur.

Bu nedenle, istek kuyruğu boş olduğunda bir GC çalışması yaparken açıkça değerdi; bunun bir dezavantajı yoktu.

Her rapor e-postayla gönderildikten sonra bir GC çalıştırması yapmaya değer olabilir, çünkü bunun bir GC çalıştırması için iyi bir zaman olduğunu biliyoruz. Ancak bilgisayarda yeterli koç varsa, GC çalışmasını geciktirerek daha iyi sonuçlar elde edilir.

Bu davranış, her rapordan sonra zorunlu bir GC'yi etkinleştiren bazı müşteriler için raporların korunmasını büyük ölçüde hızlandıran kurulum başına bazda yapılandırılmıştır. (Bunun sunucularındaki düşük hafızadan kaynaklandığını ve diğer birçok işlemi çalıştırdığını umuyorum, bu nedenle iyi bir zaman zorlamalı GC çağrıları azalttı.)

İş kuyruğu her boşken zorla çalıştırılan bir GC çalışması olduğundan hiç faydalanmayan bir kurulum tespit etmedik.

Ancak, açık bir şekilde belirtmek gerekirse, yukarıdakiler yaygın bir durum değildir.


3

Belki boktan kod yazıyorum, ama tutulma ve netbeans IDE'lerde çöp tenekesi simgesine tıklamanın 'iyi bir uygulama' olduğunu fark ettim.


1
Bu olabilir. Ancak Eclipse veya NetBeans System.gc()periyodik olarak çağrılacak şekilde programlanmışsa , davranışı can sıkıcı bulursunuz.
Stephen C

2

İlk olarak, spec ve gerçeklik arasında bir fark vardır. Spesifikasyon, System.gc () öğesinin GC'nin çalıştırması gereken bir ipucu olduğunu ve VM'nin bunu göz ardı etmekte özgür olduğunu söylüyor. Gerçek şu ki, VM asla System.gc () çağrısını görmezden gelmeyecektir .

GC'yi aramak, aramanın önemsiz bir yükü ile gelir ve bunu rastgele bir noktada yaparsanız, çabalarınız için hiçbir ödül görmeyeceksiniz. Öte yandan, doğal olarak tetiklenen bir koleksiyonun arama maliyetlerini telafi etme olasılığı yüksektir. Bir GC'nin çalıştırılması gerektiğini belirten bilginiz varsa, System.gc () öğesine çağrı yapabilirsiniz ve bunun faydalarını görmeniz gerekir. Ancak, System.gc () 'nin çağrılıp çağrılmayacağını ve ne zaman çağrıldığını anlamak için yeterli bilgiye sahip olmanız pek olası olmadığından, bunun sadece birkaç uç durumda gerçekleştiğini düşünüyorum.

Burada listelenen bir örnek, IDE'nizdeki çöp kutusuna çarpmak. Bir toplantıya gidiyorsanız neden toplantıya katılmıyorsunuz? Tepegöz sizi etkilemez ve geri döndüğünüzde yığın temizlenebilir. Bunu bir üretim sisteminde yapın ve sık sık toplama çağrıları onu bir öğütme durağına getirecektir! RMI tarafından yapılanlar gibi ara sıra yapılan aramalar bile performansı bozabilir.


3
"Gerçek şu ki, VM asla System.gc () çağrısını görmezden gelmeyecektir." - Yanlış. -XX: -DisableExplicitGC hakkında bilgi edinin.
Stephen C

1

Evet, System.gc () işlevinin çağrılması çalışacağını garanti etmez, JVM'ye göz ardı edilebilecek bir istektir. Dokümanlardan:

Gc yönteminin çağrılması, Java Sanal Makinesinin kullanılmayan nesneleri geri dönüştürmek için çaba harcadığını gösterir

Neredeyse her zaman aramak kötü bir fikirdir, çünkü otomatik bellek yönetimi genellikle ne zaman gc'den daha iyi bilir. Dahili boş bellek havuzu düşük olduğunda veya işletim sistemi bir miktar bellek geri alınmasını isterse bunu yapar.

Eğer yardımcı olduğunu biliyorsanız System.gc () öğesini çağırmak kabul edilebilir . Yani, dağıtım platformundaki her iki senaryonun davranışını kapsamlı bir şekilde test ettiniz ve ölçtünüz ve yardımcı olduğunu gösterebilirsiniz. Gc'nin kolayca tahmin edilemediğini unutmayın - bir koşuda yardımcı olabilir ve diğerine zarar verebilir.


<stroke> Ama ayrıca Javadoc'tan: _Kontrol yöntem çağrısından döndüğünde, sanal makine, hakkında yazdıklarınızın daha zorunlu bir formu olarak gördüğüm atılan tüm nesneleri geri dönüştürmek için en iyi çabayı gösterdi. </ stroke > Vidalayın, yanıltıcı olduğuna dair bir hata raporu var. Hangisi daha iyi bilir, JVM'yi ima etmenin zararları nelerdir?
zneak

1
Zarar, yanlış zamanda toplama yapmak büyük bir yavaşlama olabilir. Verdiğiniz ipucu muhtemelen kötüdür. "En iyi çaba" yorumuna gelince, deneyin ve JConsole gibi bir araçta görün. Bazen "GC Gerçekleştir" düğmesine tıklamak hiçbir şey yapmaz
tom

Kabul etmediğim için üzgünüm ama OpenJDK'de System.gc () öğesini çağırmak ve buna dayanan her şey (örneğin HP) her zaman bir çöp toplama döngüsüyle sonuçlanır. Aslında bu, IBM'in J9 uygulaması için de doğru görünüyor
Kirk

@Kirk - Hatalı: google ve -XX: -DisableExplicitGC hakkında bilgi edinin.
Stephen C

0

Deneyimlerime göre, System.gc () yöntemini etkin bir şekilde platforma özgü bir optimizasyon biçimidir (burada "platform" donanım mimarisi, işletim sistemi, JVM sürümü ve kullanılabilir RAM gibi daha fazla çalışma zamanı parametresinin birleşimidir), çünkü davranışı, belirli bir platformda kabaca tahmin edilebilir olsa da, platformlar arasında önemli ölçüde değişiklik gösterebilir (ve olacaktır).

Evet, vardır System.GC () (algılanan) performansı artıracaktır durumlar. Örneğin, gecikmeler uygulamanızın bazı bölümlerinde tolere edilebilir ancak diğerlerinde kabul edilemezse (yukarıda belirtilen oyun örneği, GC'nin seviye sırasında değil, bir seviyenin başında olmasını istediğiniz yer).

Bununla birlikte, yardımcı olup olmayacağı veya zarar göreceği (veya hiçbir şey yapmaması) platforma oldukça bağlıdır (yukarıda tanımlandığı gibi).

Bu yüzden son çare platforma özgü bir optimizasyon (yani diğer performans optimizasyonları yeterli değilse) için geçerli olduğunu düşünüyorum. Ama bunu (belirli ölçütler olmadan) yardımcı olabileceğine inandığınız için asla aramamalısınız, çünkü şansınız olmayacaktır.


0
  1. Nesneler yeni işleç kullanılarak dinamik olarak ayrıldığından,
    bu tür nesnelerin nasıl imha edildiğini ve
    daha sonra yeniden tahsis için belleklerinin serbest bırakıldığını merak ediyor olabilirsiniz .

  2. C ++ gibi bazı dillerde, dinamik olarak atanan nesnelerin bir delete operatörü kullanılarak manuel olarak serbest bırakılması gerekir.

  3. Java farklı bir yaklaşım benimser; otomatik olarak sizin için dağıtmayı yönetir.
  4. Bunu başaran tekniğe çöp toplama denir. Bu şekilde çalışır: bir nesneye referans olmadığında, o nesneye artık ihtiyaç duyulmadığı varsayılır ve nesnenin kapladığı bellek geri kazanılabilir. C ++ 'da olduğu gibi nesneleri yok etmek için açık bir ihtiyaç yoktur.
  5. Çöp toplama, programınızın yürütülmesi sırasında yalnızca (varsa) ara sıra gerçekleşir.
  6. Bu artık gerçekleşmeyecek çünkü artık kullanılmayan bir veya daha fazla nesne var.
  7. Ayrıca, farklı Java çalışma zamanı uygulamaları çöp toplama işleminde çeşitli yaklaşımlar uygulayacaktır, ancak çoğunlukla programlarınızı yazarken bunu düşünmek zorunda kalmamanız gerekir.
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.