Java'da nesneleri null değerine atamak çöp toplamayı etkiler mi?


85

nullJava'da kullanılmayan bir nesne başvurusunun atanması , çöp toplama sürecini ölçülebilir herhangi bir şekilde iyileştirir mi?

Java (ve C #) ile olan deneyimim bana, sanal makineyi veya JIT derleyicisini denemek ve alt etmek için genellikle mantıksız olduğunu öğretti, ancak iş arkadaşlarımın bu yöntemi kullandıklarını gördüm ve bunun seçilecek iyi bir uygulama olup olmadığını merak ediyorum yukarı veya şu voodoo programlama hurafelerinden biri?

Yanıtlar:


66

Tipik olarak hayır.

Ama her şey gibi: bağlıdır. Bugünlerde Java'daki GC ÇOK iyidir ve artık ulaşılamaz hale geldikten çok kısa bir süre sonra her şey temizlenmelidir. Bu, yerel değişkenler için bir yöntem bıraktıktan hemen sonra ve bir sınıf örneğine artık alanlar için başvurulmadığında gerçekleşir.

Yalnızca aksi takdirde referans olarak kalacağını biliyorsanız açıkça boş vermeniz gerekir. Örneğin etrafta tutulan bir dizi. Artık ihtiyaç duyulmadığında, dizinin tek tek öğelerini boş bırakmak isteyebilirsiniz.

Örneğin, ArrayList'teki bu kod:

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
         System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
    elementData[--size] = null; // Let gc do its work

    return oldValue;
}

Ayrıca, bir nesnenin açıkça boş bırakılması, bir nesnenin, hiç referans kalmadığı sürece doğal olarak kapsam dışına çıkmasından daha erken toplanmasına neden olmaz.

Her ikisi de:

void foo() {
   Object o = new Object();
   /// do stuff with o
}

ve:

void foo() {
   Object o = new Object();
   /// do stuff with o
   o = null;
}

İşlevsel olarak eşdeğerdir.


7
Ayrıca, büyük miktarda bellek kullanan veya yürütülmesi uzun zaman alan yöntemlerin içinde ya da uzun süre çalışabilen bir Swing uygulamasındaki kodda boş referanslar isteyebilirsiniz. Kısa yöntemlerde veya kısa ömürlü nesnelerde, fazladan kafa karıştırıcı kodlara değmez.
John Gardner

1
Öyleyse, bir nesneyi sıfırladığımızda, Çöp Toplayıcı o nesneyi hemen toplar mı?
Muhammad Babar

1
Hayır. Atık toplayıcı, referans değişkenlerinin işaret etmediği herhangi bir nesne olup olmadığını görmek için periyodik olarak çalıştırılır. Bunları null olarak ayarladığınızda ve sonra çağırdığınızda System.gc(), kaldırılacaktır (bu nesneyi işaret eden başka referans olmaması koşuluyla).
JavaTechnical

2
@JavaTechnical Bildiğim kadarıyla System.gc () 'yi çağırarak çöp toplamanın başlayacağı garanti edilmiyor.
Jack

12

Benim deneyimime göre, çoğu zaman insanlar mecburiyetten değil paranoyadan referansları boşa çıkarıyorlar. İşte hızlı bir kılavuz:

  1. Nesne A referanslar B itirazınız varsa ve artık bu başvuru ihtiyaç ve nesne Bir çöp toplama işlemi için uygun değildir o zaman açıkça alanını ortadan gerekir. Çevreleyen nesne yine de çöp toplanıyorsa, bir alanı boş bırakmaya gerek yoktur. Dispose () yönteminde alanları sıfırlamak neredeyse her zaman işe yaramaz.

  2. Bir yöntemde oluşturulan nesne referanslarını boş bırakmaya gerek yoktur. Yöntem sona erdiğinde otomatik olarak silinecekler. Bu kuralın istisnası, çok uzun bir yöntemde veya çok büyük bir döngüde çalışıyorsanız ve yöntemin sonundan önce bazı referansların temizlendiğinden emin olmanız gerekir. Yine, bu durumlar son derece nadirdir.

Çoğu zaman referansları geçersiz kılmanıza gerek kalmayacağını söyleyebilirim. Çöp toplayıcıyı zekice alt etmeye çalışmak işe yaramaz. Sonunda verimsiz, okunamayan bir kodla karşılaşacaksınız.


11

İyi makale, bugünün kodlama korkusu .

GC'nin çalışma şekli, kendilerine işaretçisi olmayan nesneleri aramaktır, aradıkları alan yığın / yığın ve sahip oldukları diğer boşluklardır. Dolayısıyla, bir değişkeni null olarak ayarlarsanız, gerçek nesne artık kimse tarafından işaret edilmez ve dolayısıyla GC olabilir.

Ancak GC tam olarak o anda çalışmayabileceğinden, aslında kendinize hiçbir şey satın almıyor olabilirsiniz. Ancak yönteminiz oldukça uzunsa (yürütme süresi açısından) buna değer olabilir, çünkü GC'nin bu nesneyi toplama şansınızı artıracaksınız.

Sorun, kod optimizasyonlarıyla da karmaşık olabilir, değişkeni null olarak ayarladıktan sonra hiçbir zaman kullanmazsanız, değeri null olarak ayarlayan satırı kaldırmak güvenli bir optimizasyon olacaktır (çalıştırılacak bir talimat eksik). Yani aslında herhangi bir gelişme elde edemeyebilirsiniz.

Yani özetle, evet yardımcı olabilir, ancak belirleyici olmayacak .



Referansı null olarak ayarlayan çizgiyi kaldıran güvenli optimizasyonun iyi bir noktası.
17:11

8

En azından javada, voodoo programlaması değil. Java'da bir nesne oluşturduğunuzda,

Foo bar = new Foo();

iki şey yaparsınız: birincisi, bir nesneye bir referans yaratırsınız ve ikincisi, Foo nesnesinin kendisini yaratırsınız. Bu veya başka bir referans var olduğu sürece, belirli nesne gc'd edilemez. ancak, bu referansa null atadığınızda ...

bar = null ;

ve başka hiçbir şeyin nesneye bir başvurusu olmadığını varsayarsak, boşaltılır ve çöp toplayıcının bir sonraki geçişinde gc için kullanılabilir.


12
Ancak kapsam dışına çıkmak, fazladan kod satırı olmadan aynı şeyi yapacaktır. Bir yöntem için yerelse, gerek yoktur. Yöntemden ayrıldıktan sonra, nesne GC için uygun olacaktır.
duffymo

2
İyi. şimdi, bir kısa sınav için, yeterli OLMAYAN bir kod örneği oluşturun. İpucu: varlar.
Charlie Martin

7

Değişir.

Genel olarak konuşursak, nesnelerinize referansları ne kadar kısa tutarsanız, daha hızlı toplanırlar.

Yönteminizin yürütülmesi 2 saniye sürüyorsa ve bir saniyelik yöntem yürütmeden sonra artık bir nesneye ihtiyacınız yoksa, ona yapılan tüm başvuruları temizlemek mantıklıdır. GC bir saniye sonra nesnenize hala referans olduğunu görürse, bir dahaki sefere bir dakika içinde kontrol edebilir.

Her neyse, tüm referansları varsayılan olarak null olarak ayarlamak benim için erken optimizasyondur ve bellek tüketimini ölçülebilir şekilde azaltmadığı belirli nadir durumlarda kimse bunu yapmamalıdır.


6

Değişkenin kapsam dışına çıkmasına izin vermek yerine açıkça null değerine bir başvuru ayarlamak, tutulan nesne çok büyük olmadığı sürece çöp toplayıcısına yardımcı olmaz, işiniz biter bitmez onu null olarak ayarlamak iyi bir fikirdir.

Genellikle referansların null olarak ayarlanması, bu nesnenin tamamen yapıldığı kodun OKUYUCUSU anlamına gelir ve artık endişelenmemelidir.

Fazladan bir diş teli seti koyarak daha dar bir kapsam sunarak benzer bir etki elde edilebilir.

{
  int l;
  {  // <- here
    String bigThing = ....;
    l = bigThing.length();
  }  // <- and here
}

bu, bigThing'in iç içe küme parantezlerinden çıktıktan hemen sonra çöpün toplanmasını sağlar.


İyi bir alternatif. Bu açıkça null değişkeni ayarlama kaçınır ve tüysüz bazı IDE değil kullanılıyor şeklindeki sıfır atama hakkında görüntülemek uyarı.
jk7

5
public class JavaMemory {
    private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);

    public void f() {
        {
            byte[] data = new byte[dataSize];
            //data = null;
        }

        byte[] data2 = new byte[dataSize];
    }

    public static void main(String[] args) {

        JavaMemory jmp = new JavaMemory();
        jmp.f();

    }

}

Yukarıdaki program atar OutOfMemoryError. Yorumunu kaldırırsanız data = null;, OutOfMemoryErrorçözülür. Kullanılmayan değişkeni null olarak ayarlamak her zaman iyi bir uygulamadır


Yani imo, saman adam argümanları dünyasına sağlam bir giriş. Değişkeni null olarak ayarlamanın o belirli durumda sorunu çözeceği bir durum yaratabildiğiniz için (ve bunu yaptığınıza ikna olmadım), onu null olarak ayarlamanın her durumda iyi bir fikir olduğu anlamına gelmez. Artık kullanmadığınız her değişkeni, kısa süre sonra kapsam dışına çıksalar bile null olarak ayarlamak için koda eklemek, sadece koda eklenir ve kodu daha az bakım yapılabilir hale getirir.
RHSeeger

Değişken verileri kapsam dışında olduğu anda neden bayt [] dizisi çöpü toplanmıyor?
Grav

2
@Grav: kapsam dışına çıkmak çöp toplayıcının yerel nesneleri toplayacağını garanti etmez. Ancak, null olarak ayarlamak OutOfMemory istisnasını engeller, çünkü GC'nin bir OutOfMemory istisnası atmaya başvurmadan önce çalışacağı ve nesne null olarak ayarlanmışsa toplanabilir olacaktır.
Stefanos T.


4

Bir keresinde bir video konferans uygulaması üzerinde çalışıyordum ve artık nesneye ihtiyacım kalmaz kalmaz boş referanslara zaman ayırdığımda performansta çok büyük bir fark olduğunu fark ettim. Bu 2003-2004 yılındaydı ve o zamandan beri GC'nin daha da akıllı hale geldiğini ancak hayal edebiliyorum. Benim durumumda, her saniye gelen ve kapsam dışına çıkan yüzlerce nesnem vardı, bu yüzden periyodik olarak devreye girdiğinde GC'yi fark ettim. Ancak, null nesneler için bir noktaya geldikten sonra, GC uygulamamı duraklatmayı durdurdu.

Yani ne yaptığına bağlı ...


2

Evet.

"The Pragmatic Programmer" s.292'den:

NULL'a bir başvuru ayarlayarak, nesneye yönelik işaretçilerin sayısını bir düşürürsünüz ... (bu, çöp toplayıcının onu kaldırmasına izin verir)


Sanırım bu sadece referans sayıldığında uygulanabilir. kullanımda mı yoksa başka türlü de?
Nrj

3
Bu tavsiye, herhangi bir modern çöp toplayıcı için geçerliliğini yitirmiştir; ve referans sayma GC'nin döngüsel referanslar gibi önemli sorunları vardır.
Lawrence Dol

Bir mark ve süpürme GC'sinde de doğru olacaktır; muhtemelen diğerlerinde. Bir referansın eksik olması, referansın sayıldığı anlamına gelmez. Tweakt'ın cevabı bunu açıklıyor. Kapsamı NULL olarak ayarlamaktansa azaltmak daha iyidir.

1

OP'nin aşağıdaki gibi şeylerden bahsettiğini varsayıyorum:

private void Blah()
{
    MyObj a;
    MyObj b;

    try {
        a = new MyObj();
        b = new MyObj;

        // do real work
    } finally {
        a = null;
        b = null;
    }
}

Bu durumda, sanal makine yine de kapsamdan çıkar çıkmaz onları GC için işaretlemez mi?

Ya da, başka bir bakış açısından, öğeleri açıkça null olarak ayarlamak, kapsamın dışına çıkmış olsalardı yapmadan önce GC'lenmelerine neden olur mu? Öyleyse, VM, belleğe zaten ihtiyaç duyulmadığında nesneyi GC yapmak için zaman harcayabilir, bu da aslında daha kötü performans CPU kullanımına neden olur çünkü daha erken GC'leme olur.


GC'nin çalıştığı saat deterministik değildir. nullGC davranışını etkileyecek bir nesne belirlemenin hiç inanmıyorum .
harto

0

" Duruma göre değişir "

Java hakkında bilgim yok ama .net'te (C #, VB.net ...) artık bir nesneye ihtiyaç duymadığınızda genellikle boş atamanıza gerek yoktur.

Ancak bunun "genellikle gerekli olmadığını" unutmayın.

.Net derleyicisi kodunuzu analiz ederek, nesnenin artık kullanılmadığını doğru bir şekilde söylemek için değişkenin yaşam süresinin iyi bir değerlendirmesini yapar. Dolayısıyla, obj = null yazarsanız, aslında obj hala kullanılıyormuş gibi görünebilir ... bu durumda bir null atamak zordur.

Bir boş atamanın aslında yardımcı olabileceği birkaç durum vardır. Bir örnek, uzun süre çalışan büyük bir kodunuz veya farklı bir iş parçacığında veya bir döngüde çalışan bir yönteme sahip olmanızdır. Bu gibi durumlarda, GC'nin artık kullanılmadığını bilmesi kolay olacak şekilde boş atamak yardımcı olabilir.

Bunun zor ve hızlı bir kuralı yok. Yukarıdaki yere gitmek kodunuzda null-atamalar yapar ve herhangi bir şekilde yardımcı olup olmadığını görmek için bir profil oluşturucu çalıştırın. Büyük olasılıkla bir fayda göremeyebilirsiniz.

Optimize etmeye çalıştığınız .net koduysa, benim deneyimim Dispose ve Finalize yöntemlerine iyi bakmanın aslında boşları rahatsız etmekten daha faydalı olduğu olmuştur.

Konuyla ilgili bazı referanslar:

http://blogs.msdn.com/csharpfaq/archive/2004/03/26/97229.aspx

http://weblogs.asp.net/pwilson/archive/2004/02/20/77422.aspx


0

Referansı geçersiz kılmak marjinal olarak daha verimli olsa bile, kodunuzu bu çirkin geçersiz kılmalarla karıştırmak zorunda kalmanın çirkinliğine değer mi? Yalnızca dağınıklık yaratacak ve onları içeren amaç kodunu belirsizleştirecektir.

Bu, Garbage toplayıcısını zekice alt etmeye çalışmaktan daha iyi bir optimizasyon adayı olmayan ender bir kod tabanıdır (hala onu alt etmeyi başaran geliştiriciler daha nadirdir). Çabalarınız büyük olasılıkla başka bir yerde harcanması, bu becerikli Xml ayrıştırıcısını atmanız veya hesaplamayı önbelleğe alma fırsatı bulmanız daha iyi olacaktır. Bu optimizasyonları ölçmek daha kolay olacak ve kod tabanınızı gürültüyle kirletmenize gerek kalmayacak.


0

Programınızın ileride çalıştırılmasında, bazı veri üyelerinin değerleri, programın dışında görünen bir çıktının bilgisayarında kullanılacaktır. Diğerleri, programın gelecekteki girdilerine (ve tahmin edilmesi imkansız) bağlı olarak kullanılabilir veya kullanılmayabilir. Diğer veri üyelerinin kullanılmayacağı garanti edilebilir. Kullanılmayan verilere tahsis edilen bellek dahil tüm kaynaklar israf edilir. Çöp toplayıcının (GC) işi boşa harcanan hafızayı ortadan kaldırmaktır. GC'nin ihtiyaç duyulan bir şeyi ortadan kaldırması felaket olur, bu nedenle kullanılan algoritma, katı minimumdan daha fazlasını koruyarak muhafazakar olabilir. Gerçekte ihtiyaç duyulmayan bazı öğeleri tutma pahasına, hızını artırmak için sezgisel optimizasyonları kullanabilir. GC'nin kullanabileceği birçok potansiyel algoritma vardır. Bu nedenle, programınızda yaptığınız değişiklikler mümkündür. ve programınızın doğruluğunu etkilemeyen, yine de GC'nin çalışmasını etkileyebilir, ya aynı işi yapmak için daha hızlı çalışmasını sağlar ya da kullanılmayan öğeleri daha erken tespit eder. Bu tür bir değişiklik, kullanılmayan bir nesne başvurusunull, Teoride her zaman büyü değil.

Bu vudu mu? Bildirildiğine göre Java kitaplığı kodunun bunu yapan kısımları var. Bu kodun yazarları, ortalama programcılardan çok daha iyidir ve çöp toplayıcı uygulamalarının ayrıntılarını bilen programcıları ya bilir ya da onlarla işbirliği yaparlar. Bu , bazen bir fayda olduğunu gösterir.


0

Dediğin gibi optimizasyonlar var, yani JVM değişkenin en son kullanıldığı yeri biliyor ve onun tarafından referans verilen nesne bu son noktadan hemen sonra GC'ye dönüştürülebilir (halen mevcut kapsamda yürütülüyor). Bu nedenle, çoğu durumda referansları geçersiz kılmak GC'ye yardımcı olmaz.

Ancak "kayırmacılık" (veya "yüzen çöp") sorunundan kaçınmak faydalı olabilir ( buradan daha fazla bilgi edinin veya videoyu izleyin ). Sorun, yığın Eski ve Genç nesillere bölündüğü ve uygulanan farklı GC mekanizmaları olduğu için var: Küçük GC (bu hızlıdır ve genellikle genç nesli temizlemek için olur) ve Major Gc (Eski genin temizlenmesi için daha uzun duraklamaya neden olur). "Adam kayırmacılık", bir Eski nesle zaten miras bırakılmış çöp tarafından atıfta bulunuluyorsa, Genç gen içindeki çöplerin toplanmasına izin vermez.

Bu "patolojik" bir durumdur çünkü HERHANGİ bir yükseltilmiş düğüm, bir GC sorunu çözene kadar takip eden TÜM düğümlerin yükselmesine neden olacaktır.

Adam kayırmayı önlemek için, kaldırılması gereken bir nesneden referansları geçersiz kılmak iyi bir fikirdir. Bu tekniğin JDK sınıflarında uygulandığını görebilirsiniz: LinkedList ve LinkedHashMap

private E unlinkFirst(Node<E> f) {
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    // ...
}
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.