2017-02-03 6 views
13

मैंने कचरा संग्रह के संबंध में कुछ वास्तव में अजीब देखा।कचरा संग्रह async विधियों

वीकआरफ विधि ऑब्जेक्ट को अपेक्षित रूप से एकत्र करता है जबकि एसिंक विधि रिपोर्ट करता है कि ऑब्जेक्ट अभी भी जिंदा है, भले ही हमने कचरा संग्रह को मजबूर किया हो। कोई विचार क्यों?

class Program 
{ 
    static void Main(string[] args) 
    { 
     WeakRef(); 
     WeakRefAsync().Wait(); 
    } 

    private static void WeakRef() 
    { 
     var foo = new Foo(); 
     WeakReference fooRef = new WeakReference(foo); 
     foo = null; 
     GC.Collect(); 
     Debug.Assert(!fooRef.IsAlive); 
    } 

    private static async Task WeakRefAsync() 
    { 
     var foo = new Foo(); 
     WeakReference fooRef = new WeakReference(foo); 
     foo = null; 
     GC.Collect(); 
     Debug.Assert(!fooRef.IsAlive); 
    } 
} 


public class Foo 
{ 

} 
+0

मजे की बात है अगर आप कचरा संग्रहण इसे एकत्र हो जाता है का इंतजार है 'का इंतजार System.Threading.Tasks.Task.Run (() => GC.Collect());' – Equalsk

+0

पहले सोचा: शायद क्योंकि 'foo' और' fooRef 'async' विधि में कंपाइलर से उत्पन्न राज्य मशीन वर्ग के गुण बन जाते हैं। लेकिन मैंने इन चरों को किसी अन्य वर्ग में लपेटकर कोशिश की और वे एकत्र हो गए .... क्या जीसी पूछने का कोई तरीका है जहां/अभी भी 'फू' इंस्टेंस का संदर्भ दे रहा है? –

+0

आईएमएचओ, आपकी विधि को कंपाइलर द्वारा कक्षा में अनुवादित किया गया है और संकलक निर्णय लेता है कि 'foo' उस वर्ग का सदस्य है। इसलिए जब तक आपकी विधि का प्रतिनिधित्व करने वाली वस्तु जीवित है, तब भी इसके सदस्य कचरा नहीं होंगे –

उत्तर

8

WeakRef विधि वस्तु की उम्मीद

के रूप में वहाँ है कि उम्मीद करने का कोई कारण नहीं जमा करता है। लिंकपैड में कोशिश कर रहा है, यह डीबग बिल्ड में नहीं होता है, उदाहरण के लिए, हालांकि डीबग और रिलीज बिल्ड दोनों के अन्य वैध संकलन या तो व्यवहार कर सकते हैं।

कंपाइलर और जिटर के बीच, वे शून्य-असाइनमेंट को ऑप्टिमाइज़ करने के लिए स्वतंत्र हैं (कुछ भी बाद में foo का उपयोग नहीं करता है), इस मामले में जीसी अभी भी ऑब्जेक्ट का संदर्भ रखने के रूप में थ्रेड को देख सकता है और नहीं इसे इकट्ठा करो। इसके विपरीत, अगर foo = null का कोई असाइनमेंट नहीं था, तो उन्हें यह एहसास हो जाएगा कि foo का उपयोग अब और नहीं किया जाता है और स्मृति का पुन: उपयोग या पंजीकरण करने के लिए इसे fooRef (या वास्तव में किसी और चीज़ के लिए) रखने के लिए पकड़ा गया था foo

तो, दोनों के साथ और foo = null बिना के बाद से इसकी वैधता की जीसी foo या तो निहित या निहित नहीं के रूप में देखने के लिए, हम यथोचित या तो व्यवहार की अपेक्षा कर सकते हैं।

फिर भी, व्यवहार देखा एक उचित क्या शायद क्या होगा के रूप में उम्मीद है, लेकिन है कि यह गारंटी नहीं है उनका कहना है लायक है।

ठीक है, उस तरफ, चलो देखते हैं कि वास्तव में यहां क्या होता है।

async विधि द्वारा उत्पादित राज्य-मशीन स्रोत में स्थानीय लोगों के अनुरूप फ़ील्ड के साथ एक संरचना है।

तो कोड:

var foo = new Foo(); 
WeakReference fooRef = new WeakReference(foo); 
foo = null; 
GC.Collect(); 

थोड़ा की तरह है:

this.foo = new Foo(); 
this.fooRef = new WeakReference(foo); 
this.foo = null; 
GC.Collect(); 

लेकिन क्षेत्र हमेशा है कुछ स्थानीय स्तर पर चल रहा एक्सेस करता है।

var temp0 = new Foo(); 
this.foo = temp0; 
var temp1 = new WeakReference(foo); 
this.fooRef = temp1; 
var temp2 = null; 
this.foo = temp2; 
GC.Collect(); 

और temp0 nulled नहीं किया गया है, इसलिए जीसी Foo के रूप में निहित पाता है: तो इस संबंध में यह लगभग की तरह है।अपने कोड की

दो दिलचस्प वेरिएंट हैं:

var foo = new Foo(); 
WeakReference fooRef = new WeakReference(foo); 
foo = null; 
await Task.Delay(0); 
GC.Collect(); 

और:

var foo = new Foo(); 
WeakReference fooRef = new WeakReference(foo); 
foo = null; 
await Task.Delay(1); 
GC.Collect(); 

जब मैंने इसे भाग गया (फिर से, कैसे स्मृति/स्थानीय लोगों के लिए रजिस्टर के साथ पेश किया गया है में उचित मतभेद हो सकता है अलग-अलग परिणामों में) पहले आपके उदाहरण का वही व्यवहार होता है, क्योंकि जब यह Task विधि और await एस में कॉल करता है, तो यह विधि एक पूर्ण कार्य देता है ताकि await तुरंत n पर चला जाए एक ही अंतर्निहित विधि कॉल के भीतर ext चीज, जो GC.Collect() है। क्योंकि उस बिंदु पर await रिटर्न और उसके बाद राज्य मशीन अपने MoveNext() विधि मोटे तौर पर एक millisecond बाद में पुन: कहा जाता है

दूसरा, Foo एकत्र देखने का व्यवहार है। चूंकि यह पीछे के दृश्यों के लिए एक नई कॉल है, इसलिए Foo का कोई स्थानीय संदर्भ नहीं है, इसलिए जीसी वास्तव में इसे एकत्र कर सकता है।

संयोग से, यह भी संभव है कि एक दिन संकलक उन स्थानीय लोगों के लिए खेतों का उत्पादन नहीं करेगा जो await सीमाओं में नहीं रहते हैं, जो एक अनुकूलन होगा जो अभी भी सही व्यवहार का उत्पादन करेगा। यदि ऐसा होता है तो आपके दो तरीके अंतर्निहित व्यवहार में बहुत समान हो जाएंगे और इसलिए मनाए गए व्यवहार में समान होने की संभावना है।

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