2017-08-03 45 views
9

, şu programı tahsil edilemediğiUlaşılamıyor nesneler çöp beklentilerimin Karşı

import java.lang.ref.WeakReference; 
import java.util.Arrays; 
import java.util.List; 

public class StackTest { 
    public static void main(String[] args) { 
    Object object1 = new Object(); 
    Object object2 = new Object(); 
    List<Object> objects = Arrays.asList(object1, object2); 

    WeakReference<Object> ref1 = new WeakReference<>(object1); 
    WeakReference<Object> ref2 = new WeakReference<>(object2); 

    for (Object o : objects) { 
     System.out.println(o); 
    } 
    objects = null; 

    object1 = null; 
    object2 = null; 

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

    System.out.println("ref1: " + ref1.get()); 
    System.out.println("ref2: " + ref2.get()); 
    } 
} 

hala GC-ed değildir object1 ve object2 anlam

ref1: [email protected] 
ref2: [email protected] 

yazdırır. Ancak, for döngüsünü programdan kaldırırken, bu nesneler GC-ed ve program yazdırılıyor olabilir. Ayrı bir yönteme for döngü taşıma

ref1: null 
ref2: null 

aynı etkiye sahiptir: nesneler programın sonunda GC-ed.

Olanlardan şüpheleniyorum, bu for döngüsünün bu nesneleri yığında saklaması ve daha sonra bunları kaldırmamasıdır. Ve nesne yığın üzerinde hala mevcut olduğundan, GC-ed olamaz. (Ben gerçekten iyi değilim) bayt koduna baktığımızda

bu hipotezi destekler niteliktedir:

53: invokeinterface #6, 1   // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 
58: astore  6 
60: aload   6 
62: invokeinterface #7, 1   // InterfaceMethod java/util/Iterator.hasNext:()Z 
67: ifeq   90 
70: aload   6 
72: invokeinterface #8, 1   // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 
77: astore  7 
79: getstatic  #9     // Field java/lang/System.out:Ljava/io/PrintStream; 
82: aload   7 
84: invokevirtual #10     // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
87: goto   60 

Ben astore komutları görüyorum, ama bayt kodu nerede bir yer nokta olamazdı bunlar tekrar yığından kaldırılır.

Ancak, benim teoriyle iki sorunlar var: O bayt kodu anlaşılan kadarıyla

  • , ben object1 (object2 tarafından üzerine) yığından kaldırılır ve son sadece olduğunu umuyordum döngüde erişilen nesne (object2) GC-ed değil.
  • programının çıkışını değişmez

    for (Object o : objects) { 
        System.out.println(o); 
        o = null; 
    } 
    

    için for döngü değiştirme. Bunun, yığındaki nesneye yapılan referansı temizleyeceğini düşünürdüm.

Soru: herkes bu for -loop bu nesneler GC-ed olamaz olmasını sağlar neden sağlam bir teorisi var? Benim teorimde birkaç boşluk var.

Bağlam: bu sorun NetBeans yöntemi NBTestCase#assertGC göre biz bellek sızıntılarını tespit için kullanılan bir birim testler, karşılaşıldı . Bu assertGC yöntemi, öbekte veya yığında hala bir nesne başvurulduğunda başarısız olur. Bizim testte

, biz kod biz for -loop kaldırıldı kadar başarısız tuttu

@Test 
public void test(){ 
    List<DisposableFoo> foos = ...; 

    doStuffWithFoo(foos); 

    List<WeakReference<DisposableFoo>> refs = ...; 

    for(DisposableFoo foo : foos){ 
    disposeFoo(foo); 
    } 

    foos = null; 
    assertGC(refs); 
} 

gibi var.

Zaten bir geçici çözümümüz var (for -loop'u ayrı bir yönteme taşıyın), ancak orijinal kodumuzun neden çalışmadığını anlamak istiyorum.

+1

Sadece "System.gc" yi çağırdığınızdan, ancak GC'nin gerçekleştiği anlamına gelmediğinden - ama emin olduğunuzu bildiğinizden eminim –

+0

@ScaryWombat: Biliyorum. Gerçek kod, birkaç kez 'System.gc' ile çağrılarak tüm yığının birkaç kez doldurulmasını sağlar. Bunu basit tutmak için bu programdan kaldırdım. Sorunu, yayınlanan kodla tutarlı bir şekilde yeniden oluşturabileceğimi ve for döngüsünü kaldırmanın sürekli çalışmasını sağladığımı unutmayın. – Robin

+0

Geleneksel döngü için –

cevap

10

Kapsam dışına çıkıncaya kadar yineleyici öğeler temizlenmez. Sorun şu ki, yığınta bir liste yineleyiciniz var. Listede yineleyicinin orijinal listesine bir referansı vardır. Listeyi, objects'u hiç boş bırakmayacakmış gibi canlı tutuyorsunuz.

adımlayıcısı, bu hasNext() yanlış dönmüştü bir kez potansiyelnull kendi iç referansını ayarlamak olabilir düzenli Iterator<E> için sonraki madde vb talep edebilirsiniz, böylece orijinal koleksiyonuna bir başvuru tutmak zorundadır ama liste yineleyicisi için her iki yönde de hareket edebildiğiniz için liste yineleyicisi bile geçerli değildir.

+0

Bu katı bir teori gibi geliyor. 'Objects.clear()' dediğimde çalıştığı gerçeği, 'object = null' çağırmadan önce bu teoriyi doğrular. – Robin

+3

Bir kez 'hasNext()' 'false'ı döndürdüğü anlamına mı geliyor? – Kayaman

+1

Yaptığını farz ettim. Ama kim Jon Skeet'in bir cevabını düzenlemeye cüret ederse ... – Robin

1

Değişim for döngüsü ve daha sonra geleneksel bir döngü kullanarak dışında form tekrar

for (int i = 0; i < objects.size(); i++) 
{ 
    Object o = objects.get(i); 
    System.out.println(o); 
} 
+0

Bu gerçekten işe yarıyor. Ama zaten bir çözümüm var. Benim sorum gerçekten benim yapmamın işe yaramaması… 'for' döngüsünün neden bu nesnelerin GC-ed olamayacağını bilmesini anlamıyorum. – Robin

+0

Çalıştığına sevindim. Bir sonraki bölüm şimdi anladım. Herhangi bir sonuca vardığımda cevabımı güncelleyeceğim. – Adeel

1

deneyin, wrap olabilir senin for-loop yüzden kapsam dışına gider ve

{ 
for (Object o : objects) { 
    System.out.println(o); 
} 
} 

oluşturulan toplanan gets

+0

Derleyicinin fazladan kapsam belirleme işlemini optimize edeceğini ve sonuçların aynı olacağını düşünüyorum. – TruckDriver

+0

Derleyicinin kapsamı değiştireceğini bilmiyordum. Yukarıdaki işe yaramazsa, kesinlikle bir "try-finally" bloğu –

+3

@Timothy olur: derlenmiş kodda hiçbir kapsam yoktur. Kavramsal olarak yineleyici, döngüden sonra bile olsa, kaşlı ayraçlar olmasa bile kapsam dışında olur, ancak yeni bir yerel değişken yuvasını yeniden kullanmadığı sürece, referans yığın çerçevede sallanır. – Holger