2017-08-03 33 views
9

मेरी उम्मीदों के खिलाफ, निम्नलिखित कार्यक्रमपहुंचा नहीं जा सकता वस्तुओं कचरा एकत्र नहीं किया जा सकता

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()); 
    } 
} 

अभी भी

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

object1 और object2 अर्थ जीसी एड नहीं कर रहे हैं बाहर प्रिंट करता है।

हालांकि, प्रोग्राम से for पाश को हटाते समय, वे ऑब्जेक्ट्स जीसी-एड और प्रोग्राम प्रिंट हो सकते हैं।

ref1: null 
ref2: null 

एक अलग विधि से for पाश आगे बढ़ते ही प्रभाव पड़ता है: वस्तुओं कार्यक्रम के अंत में जीसी एड कर रहे हैं।

मुझे क्या संदेह है कि यह हो रहा है कि for लूप उन वस्तुओं को ढेर पर संग्रहीत करता है, और बाद में उन्हें हटा नहीं देता है। और चूंकि वस्तु अभी भी ढेर पर मौजूद है, यह जीसी-एड नहीं हो सकती है।

बाइट कोड को देखते हुए (जो मैं वास्तव में उस पर अच्छा नहीं कर रहा हूँ) इस परिकल्पना का समर्थन करने के लिए लगता है:

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 

मैं astore आदेश देखें, लेकिन मैं बाइट कोड जहां में एक जगह का पता नहीं कर सका उनको ढेर से हटा दिया जाता है।

हालांकि, मैं दो मेरी सिद्धांत के साथ समस्या है:

  • मैं क्या है कि बाइट कोड से समझ में आ से, मैं उम्मीद है कि object1 ढेर (object2 द्वारा ओवरराइट) से निकाल दिया जाता है, और केवल पिछले लूप में एक्सेस किया गया ऑब्जेक्ट (object2) जीसी-एड नहीं होगा।
  • for (Object o : objects) { 
        System.out.println(o); 
        o = null; 
    } 
    

    करने के लिए for पाश बदलने कार्यक्रम के उत्पादन में परिवर्तित नहीं होता। मैंने सोचा होगा कि उस ढेर पर वस्तु के संदर्भ को स्पष्ट कर देगा।

प्रश्न: किसी को भी क्यों कि for -loop सुनिश्चित करता है कि उन वस्तुओं जीसी एड नहीं किया जा सकता पर एक ठोस सिद्धांत है? मेरे सिद्धांत में उनमें कुछ अंतर छेद हैं।

प्रसंग: यह समस्या है जो हम स्मृति रिसाव का पता लगाने के लिए उपयोग एक इकाई परीक्षण, Netbeans विधि NBTestCase#assertGC के आधार पर में आई थी। यह assertGC विधि विफल हो जाएगी जब किसी ऑब्जेक्ट को अभी भी ढेर या ढेर पर संदर्भित किया जाता है।

हमारे परीक्षण में, हम

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

    doStuffWithFoo(foos); 

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

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

    foos = null; 
    assertGC(refs); 
} 

जो नाकाम रहने पर रखा, जब तक हम for -loop हटाया तरह कोड है।

हमारे पास पहले से ही एक वर्कअराउंड है (for - एक अलग विधि पर जाएं), लेकिन मैं समझना चाहता हूं कि हमारा मूल कोड क्यों काम नहीं करता है।

+1

सिर्फ इसलिए कि आप 'System.gc' कहते हैं लेकिन इसका मतलब यह नहीं है कि जीसी ने खुश किया है - लेकिन मुझे यकीन है कि आप जानते हैं कि –

+0

@ScaryWombat: मुझे पता है। असली कोड कुछ समय में पूरे सिस्टम को 'System.gc' को कॉल करने के साथ मिलाकर पूरे ढेर को भर देता है। मैंने इसे सरल रखने के लिए इस कार्यक्रम से हटा दिया। ध्यान दें कि मैं लगातार पोस्ट कोड के साथ समस्या को पुन: उत्पन्न कर सकता हूं, और लूप को हटाने के लिए लगातार काम करता है। – Robin

+0

यदि आप पारंपरिक लूप –

उत्तर

10

समस्या यह है कि आपके पास अभी भी स्टैक पर एक सूची इटरेटर है, और वें सूची में इटेटर के मूल सूची का संदर्भ है। यह सूची को जिंदा रखता है जैसे कि आप कभी भी objects को शून्य पर सेट नहीं करेंगे।

एक इटरेटर, मूल संग्रह के लिए एक संदर्भ रखने के लिए, इतना है कि यह एक नियमित रूप से Iterator<E> लिए अगले आइटम आदि यह संभावितnull करने के लिए अपने आंतरिक संदर्भ सेट कर सकते हैं एक बार hasNext() झूठी लौटा था अनुरोध कर सकते हैं है, लेकिन एक सूची इटेटरेटर के लिए भी यह मामला नहीं है, क्योंकि आप एक सूची इटरेटर के भीतर दोनों दिशाओं में स्थानांतरित कर सकते हैं।

+0

यह एक ठोस सिद्धांत की तरह लगता है। तथ्य यह है कि जब मैं 'ऑब्जेक्ट्स = नल' को कॉल करने से पहले 'ऑब्जेक्ट्स। स्क्लर()' कहता हूं तो यह सिद्धांत इस सिद्धांत की पुष्टि करता है। – Robin

+3

आपका मतलब है कि एक बार 'है अगला()' 'false' वापस आ गया है? – Kayaman

+1

मुझे लगा कि वह करता है। लेकिन जो जॉन स्कीट के जवाब को संपादित करने की हिम्मत करता है ... – Robin

1

बदलें अपने पाश के लिए और उसके बाद फिर

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

वह वास्तव में काम करता है। लेकिन मेरे पास पहले से ही एक कामकाज है। मेरा सवाल वास्तव में क्यों है कि मेरा निर्माण काम नहीं करता है ... मैं समझने में असफल रहा हूं कि 'लूप' क्यों सुनिश्चित करता है कि वे वस्तुएं जीसी-एड नहीं हो सकती हैं। – Robin

+0

खुशी है कि यह काम किया। अगले भाग में मैं अब पता लगा रहा हूँ। एक बार जब मैं किसी भी निष्कर्ष तक पहुंच जाता हूं तो मैं अपना जवाब अपडेट करूंगा। – Adeel

1

एक पारंपरिक पाश का उपयोग कर प्रयास के अलावा फार्म, आप wrap सकता है आपके for-loop इतना है कि यह क्षेत्र से बाहर चला जाता है और एकत्र हो जाता है

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

उत्पन्न इटेटरेटर सामग्री को तब तक साफ़ नहीं किया जाएगा जब तक यह दायरे से बाहर नहीं निकलता

+0

मुझे लगता है कि संकलक उस अतिरिक्त स्कोपिंग को अनुकूलित कर देगा और नतीजा वही होगा। – TruckDriver

+0

नहीं पता था कि कंपाइलर गुंजाइश बदल जाएगा। यदि उपर्युक्त काम नहीं करता है, तो निश्चित रूप से 'try-end' ब्लॉक –

+3

@ टिमोथी: संकलित कोड में कोई स्कोपिंग नहीं है। संकल्पनात्मक रूप से, लेटर के बाद भी इटेटरेटर गुंजाइश से बाहर हो जाएगा, यहां तक ​​कि घुंघराले ब्रेसिज़ के बिना भी, लेकिन जब तक कोई नया स्थानीय चर इसके स्लॉट का उपयोग नहीं करता है, तो संदर्भ स्टैक फ्रेम में लटक रहा है। – Holger

संबंधित मुद्दे