2015-10-06 10 views
10

मैं एक सी #, यूडब्ल्यूपी 10 समाधान विकसित कर रहा हूं जो एक तेज, निरंतर पढ़ने/लिखने के लूप का उपयोग कर नेटवर्क डिवाइस के साथ संचार करता है। एपीआई द्वारा प्रस्तावित स्ट्रीमस्केट बहुत अच्छा काम करता था, जब तक मुझे एहसास हुआ कि स्मृति मेमोरी थी: सैकड़ों मिनट के क्रम में, ढेर में Task<uint32> का संचय होता है।एक लूप में StreamSocket का उपयोग क्यों स्मृति रिसाव का कारण बनता है?

मैं एक async Task अंदर एक सादे पुराने while (true) पाश, या एक आत्म पोस्टिंग TPL Dataflow साथ ActionBlock<T> (प्रति this answer के रूप में), परिणाम का उपयोग कर एक ही है का उपयोग करें।

मैं आगे समस्या को अलग करने की अगर मैं सॉकेट से पढ़ने को खत्म करने और लेखन पर ध्यान केंद्रित करने में सक्षम हूँ: मैं DataWriter.StoreAsync दृष्टिकोण या अधिक प्रत्यक्ष StreamSocket.OutputStream.WriteAsync(IBuffer buffer) का उपयोग हैं, समस्या बनी हुई है। इसके अलावा, .AsTask() को जोड़ने से कोई फर्क नहीं पड़ता।

कचरा कलेक्टर चलाने पर भी, इन Task<uint32> को कभी भी ढेर से हटाया नहीं जाता है। ये सभी कार्य पूर्ण हैं (RanToCompletion), इसमें कोई त्रुटि या कोई अन्य संपत्ति मान नहीं है जो "पुनः दावा करने के लिए तैयार नहीं है" इंगित करेगा।

this page पर मेरी समस्या का संकेत मिलता है (प्रबंधित से अप्रबंधित दुनिया से चलने वाला एक बाइट सरणी स्मृति की रिहाई को रोकता है), लेकिन निर्धारित समाधान बहुत स्पष्ट लगता है: इस के आसपास एकमात्र तरीका सभी को लिखना है सी ++/सीएक्स में संचार तर्क। मुझे उम्मीद है कि यह सच नहीं है; निश्चित रूप से अन्य सी # डेवलपर्स ने मेमोरी लीक के बिना लगातार उच्च गति नेटवर्क संचारों को सफलतापूर्वक महसूस किया है। और निश्चित रूप से माइक्रोसॉफ्ट मेमोरी लीक सी ++/CX में

संपादित

का अनुरोध किया, कुछ नमूना कोड के रूप में बिना कोई API कि केवल काम करता है रिलीज नहीं होगी। मेरे अपने कोड में बहुत सारी परतें हैं, लेकिन this Microsoft sample के साथ एक बहुत ही सरल उदाहरण देखा जा सकता है। मैंने समस्या को उजागर करने के लिए लूप में 1000 बार भेजने के लिए एक सरल संशोधन किया।

public sealed partial class Scenario3 : Page 
{ 
    // some code omitted 

    private async void SendHello_Click(object sender, RoutedEventArgs e) 
    { 
     // some code omitted 

     StreamSocket socket = //get global object; socket is already connected 

     DataWriter writer = new DataWriter(socket.OutputStream); 

     for (int i = 0; i < 1000; i++) 
     { 
      string stringToSend = "Hello"; 
      writer.WriteUInt32(writer.MeasureString(stringToSend)); 
      writer.WriteString(stringToSend); 
      await writer.StoreAsync(); 
     } 
    } 
} 

एप्लिकेशन को शुरू करने और सॉकेट जोड़ने पर, वहाँ Task<UInt32> ढेर पर की केवल उदाहरण है: यह प्रासंगिक कोड है। "SendHello" बटन पर क्लिक करने के बाद, 86 उदाहरण हैं। इसे दूसरी बार दबाकर: 12 9 उदाहरण।

# संपादित 2 मेरे एप्लिकेशन चलाने के बाद (तंग पाश भेजने के साथ/प्राप्त) 3 घंटे के लिए, मैं देख सकता है कि वहाँ निश्चित रूप से एक समस्या है: 0.5 लाख टास्क उदाहरणों, जो GC'd कभी नहीं मिलता है, और ऐप की प्रक्रिया मेमोरी प्रारंभिक 46 एमबी से 105 एमबी तक बढ़ी। जाहिर है यह ऐप अनिश्चितता से नहीं चल सकता है। हालांकि ... यह केवल डीबग मोड में चलने पर लागू होता है। अगर मैं रिलीज मोड में अपना ऐप संकलित करता हूं, इसे तैनात करता हूं और इसे चलाता हूं, तो कोई मेमोरी समस्या नहीं होती है। मैं इसे पूरी रात चल सकता हूं और यह स्पष्ट है कि स्मृति ठीक से प्रबंधित की जा रही है। केस बंद हुआ।

+1

समस्या प्रदर्शित करने वाले कोड को पोस्ट करें – alexm

+0

@alexm, कृपया पोस्ट कोड देखें। Thx – BCA

+0

@BCA - ईवेंट हैंडलर में वैश्विक ऑब्जेक्ट का संदर्भ दे रहे समय पर यह कचरा पृष्ठ कैसे एकत्र कर सकता है? –

उत्तर

10

86 उदाहरण हैं। इसे दूसरी बार दबाकर: 12 9 उदाहरण।

यह पूरी तरह से सामान्य है।और एक मजबूत संकेत यह है कि असली समस्या यह है कि आप नहीं जानते कि मेमोरी प्रोफाइलर रिपोर्ट को सही ढंग से कैसे समझें।

कार्य लगता है एक बहुत ही महंगी वस्तु की तरह लगता है, इसमें हिरन के लिए बहुत धमाका है और एक धागा शामिल है, सबसे महंगा ऑपरेटिंग सिस्टम ऑब्जेक्ट जो आप कभी भी बना सकते हैं। लेकिन ऐसा नहीं है, एक कार्य वस्तु वास्तव में एक puny वस्तु है। इसमें केवल 32-बिट मोड में 44 बाइट्स, 64-बिट मोड में 80 बाइट्स लगते हैं। वास्तव में महंगा संसाधन टास्क के स्वामित्व में नहीं है, थ्रेडपूल प्रबंधक इसका ख्याल रखता है।

इसका मतलब है कि आप संग्रह वस्तुओं को ट्रिगर करने के लिए जीसी ढेर पर पर्याप्त दबाव डालने से पहले लॉक ऑब्जेक्ट्स के बना सकते हैं। 32-बिट मोड में जेन # 0 सेगमेंट भरने के लिए 47 हजार उनमें से। सर्वर पर कई और, सैकड़ों हजारों, इसके सेगमेंट बहुत बड़े हैं।

अपने कोड स्निपेट में, कार्य ऑब्जेक्ट्स केवल वही वस्तुएं हैं जिन्हें आप वास्तव में बनाते हैं। आपके लिए (;;) लूप इसलिए लगभग लूप नहीं करता है जो अक्सर कम करने या सीमित करने वाली कार्य वस्तुओं की संख्या को देखने के लिए पर्याप्त होता है।

तो यह सामान्य कहानी है, एनईटी फ्रेमवर्क के रिसाव के आरोप, विशेष रूप से इन प्रकार के आवश्यक ऑब्जेक्ट प्रकारों पर जो महीनों तक चलने वाले सर्वर-स्टाइल ऐप्स में भारी रूप से उपयोग किए जाते हैं, हमेशा अति अतिरंजित होते हैं। कचरा कलेक्टर का डबल अनुमान लगाना हमेशा मुश्किल होता है, आप आम तौर पर केवल अपने ऐप को महीनों तक चलते हुए विश्वास करते हैं और कभी भी ओओएम में असफल नहीं होते हैं।

+0

बहुत दिलचस्प है। मैं अभी अपना परीक्षण ऐप एक टेस्ट ड्राइव के लिए ले रहा हूं। एक घंटे के बाद मेरे पास 100,000 से अधिक कार्य वस्तुओं और गिनती है। यह दिलचस्प है कि कचरा संग्रह हर कुछ सेकंड में हो रहा है, लेकिन इन कार्य वस्तुओं को कभी साफ नहीं किया जाता है। लेकिन आप कह रहे हैं कि यह सामान्य है और मुझे अपने ऐप को ओओएम तक चलाने देना चाहिए यदि मुझे विश्वास नहीं है? – BCA

+0

आप [जीसी.कोलेक्ट] (https://msdn.microsoft.com/en-us/library/xe0c2357 (v = vs.110) .aspx पर कॉल कर सकते हैं) एक कचरा संग्रह को मजबूर करने के लिए यह देखने के लिए कि क्या यह उन उदाहरणों को साफ़ करता है । अगर ऐसा होता है, तो मुझे नहीं लगता कि आपको इसके बारे में चिंता करने की ज़रूरत है। – Damyan

0

मैं डेटावाइटर को इसके अंदर और बंद कर दूंगा।

+0

मैंने ऐसा किया। प्रत्येक पुनरावृत्ति में सॉकेट के लिए एक ब्लॉक का उपयोग शामिल था – BCA

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