क्यों लूप के दौरान TextBox.Text को जोड़ना प्रत्येक पुनरावृत्ति के साथ अधिक मेमोरी लेता है?


82

लघु प्रश्न

मेरे पास एक लूप है जो 180,000 बार चलता है। प्रत्येक पुनरावृत्ति के अंत में परिणामों को टेक्स्टबॉक्स में जोड़ना चाहिए, जिसे वास्तविक समय में अपडेट किया जाता है।

उपयोग MyTextBox.Text += someValueकरने से एप्लिकेशन को भारी मात्रा में मेमोरी खाने का कारण बनता है, और यह कुछ हजार रिकॉर्ड के बाद उपलब्ध मेमोरी से बाहर निकल जाता है।

क्या पाठ को TextBox.Text180,000 बार लागू करने का एक अधिक कुशल तरीका है ?

संपादित करें मुझे वास्तव में इस विशिष्ट मामले के परिणाम की परवाह नहीं है, हालांकि मैं जानना चाहता हूं कि यह स्मृति हॉग क्यों लगता है, और अगर पाठ को टेक्स्टबॉक्स में जोड़ने के लिए अधिक कुशल तरीका है।


दीर्घ (मूल) प्रश्न

मेरे पास एक छोटा ऐप है जो सीएसवी फ़ाइल में आईडी नंबरों की एक सूची पढ़ता है और प्रत्येक के लिए एक पीडीएफ रिपोर्ट बनाता है। प्रत्येक पीडीएफ फाइल जेनरेट होने के बाद, ResultsTextBox.Textसंसाधित होने वाली रिपोर्ट की आईडी संख्या के साथ जोड़ दिया जाता है और इसे सफलतापूर्वक संसाधित किया जाता है। प्रक्रिया एक पृष्ठभूमि थ्रेड पर चलती है, इसलिए ResultsTextBox अद्यतन वास्तविक समय हो जाता है क्योंकि आइटम संसाधित हो जाते हैं

मैं वर्तमान में 180,000 आईडी नंबरों के खिलाफ ऐप चला रहा हूं, हालांकि जिस मेमोरी को एप्लिकेशन ले रहा है वह समय के साथ तेजी से बढ़ रहा है। यह लगभग 90K से शुरू होता है, लेकिन लगभग 3000 रिकॉर्ड द्वारा यह लगभग 250MB तक ले जा रहा है और 4000 रिकॉर्ड के अनुसार यह एप्लिकेशन लगभग 500 MB मेमोरी ले रहा है।

यदि मैं परिणाम टेक्स्टबॉक्स में अपडेट की टिप्पणी करता हूं, तो स्मृति लगभग 90K पर स्थिर रहती है, इसलिए मैं मान सकता हूं कि लेखन ResultsText.Text += someValueवह है जो स्मृति को खाने के लिए पैदा कर रहा है।

मेरा सवाल यह है कि यह क्यों है? टेक्स्टबॉक्स में डेटा को जोड़ने का एक बेहतर तरीका क्या है। यह मेमोरी नहीं खाती है?

मेरा कोड इस तरह दिखता है:

try
{
    report.SetParameterValue("Id", id);

    report.ExportToDisk(ExportFormatType.PortableDocFormat,
        string.Format(@"{0}\{1}.pdf", new object[] { outputLocation, id}));

    // ResultsText.Text += string.Format("Exported {0}\r\n", id);
}
catch (Exception ex)
{
    ErrorsText.Text += string.Format("Failed to export {0}: {1}\r\n", 
        new object[] { id, ex.Message });
}

यह भी ध्यान देने योग्य होना चाहिए कि ऐप एक बार की चीज है और इससे कोई फर्क नहीं पड़ता कि सभी रिपोर्ट तैयार करने में कुछ घंटे (या दिन :)) लग रहे हैं। मेरी मुख्य चिंता यह है कि यदि यह सिस्टम मेमोरी सीमा को हिट करता है, तो यह चलना बंद हो जाएगा।

परिणाम को अपडेट करने वाली लाइन को छोड़ने के साथ मैं ठीक हूं, टेक्स्टबॉक्स ने इस बात को चलाने के लिए टिप्पणी की, लेकिन मैं यह जानना चाहूंगा कि क्या TextBox.Textभविष्य की परियोजनाओं के लिए डेटा को जोड़ने के लिए अधिक स्मृति कुशल तरीका है ।


7
आप StringBuilderपाठ को जोड़ने के लिए एक का उपयोग करने की कोशिश कर सकते हैं , फिर पूरा होने पर, StringBuilderपाठ बॉक्स को मान असाइन करें ।
कीबोर्ड

1
मुझे नहीं पता कि यह कुछ भी बदलेगा, लेकिन क्या होगा यदि आपके पास एक स्ट्रिंगबर्ल होगा जो नए आईडी-एस को जोड़ता है और आप एक संपत्ति का उपयोग करेंगे जो स्ट्रिंग बिल्डर के नए मूल्य के साथ अद्यतन हो जाती है और इसे आपके टेक्स्टबॉक्स पर बाँध देती है। संपत्ति।
बिग एल

2
जब आप स्ट्रिंग को कॉल कर रहे हैं तो आप ऑब्जेक्ट ऐरे को इनिशियलाइज़ क्यों कर रहे हैं। ओवरलोड हैं जो 2 पैरामीटर लेते हैं ताकि आप एक सरणी बनाने से बच सकें। इसके अलावा जब आप पैरामेंट्स को ओवरलोड करते हैं तो व्यू के पीछे आपके लिए एरे बनाया जाता है।
चोसपंडियन

1
स्ट्रिंग कॉनसैट अकुशल नहीं है। यदि आप कार्य की कई इकाइयों में स्ट्रेंथनिंग कर रहे हैं और कार्य की प्रत्येक इकाई के बीच परिणाम प्रदर्शित कर रहे हैं, तो यह StringBuilder की तुलना में अधिक कुशल होगा। StringBuilder वास्तव में केवल तभी अधिक कुशल होती है जब आप लूप के माध्यम से एक स्ट्रिंग का निर्माण कर रहे होते हैं और फिर केवल लूप के अंत में परिणाम लिखते हैं।
जेम्स माइकल हरे

3
मैं कहने जा रहा था, यह काफी प्रभावशाली मशीन है :-)
जेम्स माइकल हरे

जवाबों:


119

मुझे संदेह है कि स्मृति का उपयोग इतना बड़ा है क्योंकि टेक्स्टबॉक्स एक स्टैक को बनाए रखता है ताकि उपयोगकर्ता पाठ को पूर्ववत् / फिर से कर सके। यह सुविधा आपके मामले में आवश्यक नहीं लगती है, इसलिए IsUndoEnabledझूठी सेटिंग पर जाएं।


1
MSDN लिंक से: "मेमोरी लीक यदि आपके पास आपके एप्लिकेशन में मेमोरी बढ़ रही है क्योंकि आप कोड से बहुत बार मूल्य निर्धारित करते हैं, तो टेक्स्टब्लॉक का पूर्ववत स्टैक मेमोरी का" लीक "हो सकता है। इस संपत्ति का उपयोग करके आप अक्षम कर सकते हैं। यह स्मृति को लीक करने का रास्ता साफ कर रहा है। ”
तरंग

33
अधिकांश समय, उपयोगकर्ता और डेवलपर्स टेक्स्टबॉक्स को मानक टेक्स्टबॉक्स (जैसे पूर्ववत / फिर से करने की क्षमता के साथ) की तरह कार्य करने की उम्मीद करेंगे। ओपी की आवश्यकताओं जैसे किनारे के मामलों में, यह एक बाधा साबित हो सकता है। यदि अधिकांश लोग इसका उपयोग करते हैं, तो यह डिफ़ॉल्ट होना चाहिए। आप मानक कार्यक्षमता को ऑप्ट-इन करने के लिए बाध्य करने के लिए किनारे के मामले की अपेक्षा क्यों करेंगे?
keyboardP

1
वैकल्पिक रूप से, आप UndoLimitयथार्थवादी मूल्य भी निर्धारित कर सकते हैं । -1 का डिफ़ॉल्ट एक असीमित स्टैक इंगित करता है। शून्य (0) भी पूर्ववत अक्षम कर देगा।
मायर्मियन जूल 18'12

14

के TextBox.AppendText(someValue)बजाय का उपयोग करें TextBox.Text += someValue। यह पाठ बॉक्स पर नहीं, क्योंकि TextBox.Text पर याद करना आसान है। स्ट्रिंगबर्ल की तरह, यह हर बार जब आप कुछ जोड़ते हैं तो पूरे पाठ की प्रतियां बनाने से बचेंगे।

यह देखना दिलचस्प होगा कि यह IsUndoEnabledकीबोर्ड के जवाब से ध्वज की तुलना कैसे करता है ।


विंडोज़ के रूपों के मामले में, यह सबसे अच्छा समाधान है, क्योंकि विंडोज़ के फॉर्म में टेक्स्ट बॉक्स नहीं है। SsndndEnabled
BrDaHa

विन रूपों में, आपके पास एक bool CanUndoसंपत्ति है
imlokesh

9

सीधे टेक्स्ट प्रॉपर्टी पर न जोड़ें। एप्लाइड के लिए एक स्ट्रिंगबर्ल का उपयोग करें, फिर जब किया जाता है, तो स्ट्रिंगर से तैयार स्ट्रिंग के लिए सेट करें।


2
मैं एक पृष्ठभूमि धागे पर लूप रन का उल्लेख करना भूल गया और परिणामों को वास्तविक समय अपडेट किया गया
राहेल

5

पाठ बॉक्स का उपयोग करने के बजाय मैं निम्नलिखित कार्य करूंगा:

  1. एक टेक्स्ट फ़ाइल खोलें और केवल मामले में लॉग फ़ाइल में त्रुटियों को स्ट्रीम करें।
  2. संभावित रूप से बड़े पैमाने पर स्ट्रिंग की प्रतिलिपि बनाने से बचने के लिए त्रुटियों का प्रतिनिधित्व करने के लिए एक सूची बॉक्स नियंत्रण का उपयोग करें।

4

व्यक्तिगत रूप से, मैं हमेशा string.Concat* का उपयोग करता हूं । मुझे याद है कि स्टैक ओवरफ्लो पर सालों पहले एक प्रश्न पढ़ा था जिसमें प्रोफाइलिंग के आँकड़े थे जो आमतौर पर इस्तेमाल किए जाने वाले तरीकों की तुलना करते थे, और (प्रतीत होता है) याद करने के लिए string.Concat

बहरहाल, सबसे अच्छा मुझे मिल सकता है यह संदर्भ प्रश्न है और यह विशिष्ट String.FormatबनामStringBuilder प्रश्न है, जिसमें उल्लेख है कि आंतरिक रूप से String.Formatउपयोग करता है StringBuilder। इससे मुझे आश्चर्य होता है कि क्या आपकी मेमोरी हॉग कहीं और निहित है।

** जेम्स की टिप्पणी के आधार पर, मुझे यह उल्लेख करना चाहिए कि मैं कभी भी भारी स्ट्रिंग प्रारूपण नहीं करता हूं, क्योंकि मैं वेब आधारित विकास पर ध्यान केंद्रित करता हूं। *


मैं मानता हूं, कभी-कभी लोगों को यह कहने की ललक में हो जाता है कि "हमेशा एक्स क्यूज़ एक्स का उपयोग करना सबसे अच्छा है" जो आमतौर पर एक ओवरसाइम्प्लिफिकेशन है। String.Concat (), string.Format () और StringBuilder के बीच बहुत सूक्ष्मता है। मेरे अंगूठे का नियम प्रत्येक का उपयोग करता है जो इसके लिए इरादा है (यह मूर्खतापूर्ण लगता है, मुझे पता है, लेकिन यह सच है)। जब मैं तार जोड़ रहा हूं (और फिर तुरंत परिणाम का उपयोग कर रहा हूं), तो मैं उस प्रारूप का उपयोग करता हूं जब मैं गैर-तुच्छ स्ट्रिंग प्रारूपण (पैडिंग, संख्यात्मक प्रारूप, आदि) का प्रदर्शन कर रहा हूं, और स्ट्रिंग के निर्माण के लिए स्ट्रिंगब्यूलर को लूप के दौरान तैयार करता हूं लूप के अंत में उपयोग किया जा सकता है।
जेम्स माइकल हरे

@JamesMichaelHare, कि मुझे समझ में आता है; क्या आप सुझाव दे रहे हैं कि string.Format/ StringBuilderका उपयोग यहां अधिक उचित है?
जवाहरन

अरे नहीं, मैं सिर्फ आपके सामान्य बिंदु से सहमत था कि आमतौर पर सरल स्ट्रिंग के लिए कॉनैट सबसे अच्छा है। "अंगूठे के नियमों" के साथ समस्या यह है कि वे बीसीएल संस्करण से संस्करण में बदल सकते हैं यदि बीसीएल बदल जाता है, इस प्रकार तार्किक रूप से सही निर्माण के साथ चिपके रहना अधिक बनाए रखने योग्य है और आमतौर पर अपने कार्यों के लिए बेहतर प्रदर्शन करता है। मेरे पास वास्तव में एक पुराना ब्लॉग पोस्ट था जहाँ मैंने तीनों की तुलना यहाँ की: geekswithblogs.net/BlackRabbitCoder/archive/2010/05/10/…
James Michael Hare

विधिवत उल्लेख - बस सुनिश्चित होना चाहता था - और "हमेशा" शब्द के मेरे उपयोग को प्राप्त करने के लिए संपादित उत्तर।
जवाहरन

3

शायद TextBox पर पुनर्विचार करें? एक ListBox होल्डिंग स्ट्रिंग आइटम शायद बेहतर प्रदर्शन करेंगे।

लेकिन मुख्य समस्या आवश्यकताओं की प्रतीत होती है, 180,000 वस्तुओं को दिखाना एक (मानव) उपयोगकर्ता के उद्देश्य से नहीं हो सकता है, न ही इसे "अतिरिक्त समय" में बदल रहा है।

बेहतर तरीका डेटा या प्रगति संकेतक का एक नमूना दिखाना होगा।

जब आप इसे गरीब उपयोगकर्ता, बैच स्ट्रिंग अपडेट में डंप करना चाहते हैं। कोई भी उपयोगकर्ता 2 या 3 से अधिक परिवर्तन प्रति सेकंड नहीं कर सकता है। इसलिए यदि आप 100 / सेकंड का उत्पादन करते हैं, तो 50 का समूह बनाएं।


धन्यवाद हेनक। यह एक बार की बात थी इसलिए इसे लिखते समय मैं आलसी हो रहा था। मुझे पता था कि स्थिति क्या है, यह जानने के लिए मैं किसी तरह का विज़ुअल आउटपुट चाहता था, और मुझे टेक्स्ट सेलेक्शन क्षमताएं और एक स्क्रॉलबार चाहिए था। मुझे लगता है कि मैं एक स्क्रॉलव्यूअर / लेबल का उपयोग कर सकता था, लेकिन टेक्स्टबॉक्स में स्क्रॉलबर्स बने हैं। मुझे उम्मीद नहीं थी कि यह समस्या पैदा करेगा :)
राहेल

2

कुछ प्रतिक्रियाओं ने इस पर विचार किया है, लेकिन किसी ने भी इसे स्पष्ट रूप से नहीं कहा है जो आश्चर्य की बात है। स्ट्रिंग्स अपरिवर्तनीय हैं जिसका मतलब है कि एक स्ट्रिंग को बनाया जाने के बाद संशोधित नहीं किया जा सकता है। इसलिए, हर बार जब आप किसी मौजूदा स्ट्रिंग को संक्षिप्त करते हैं, तो एक नए स्ट्रिंग ऑब्जेक्ट को बनाने की आवश्यकता होती है। उस स्ट्रिंग ऑब्जेक्ट से जुड़ी मेमोरी को भी स्पष्ट रूप से बनाने की आवश्यकता होती है, जो महंगी हो सकती है क्योंकि आपके स्ट्रिंग्स बड़े और बड़े होते जाते हैं। कॉलेज में, मैंने एक बार एक जावा प्रोग्राम में स्ट्रिंग्स को समेटने की शौकिया गलती की, जो हफ़मैन कोडिंग संपीड़न था। जब आप पाठ की बहुत बड़ी मात्रा में सम्‍मिलित कर रहे हैं, तो स्ट्रिंग संकेतन वास्तव में आपको चोट पहुँचा सकता है, जब आप बस स्ट्रिंगबर्ल का उपयोग कर सकते थे, जैसा कि यहां कुछ ने उल्लेख किया है।


2

जैसा सुझाव दिया गया है, स्ट्रिंग स्ट्रिंग का उपयोग करें। अंतिम स्ट्रिंग के आकार का अनुमान लगाने की कोशिश करें, फिर उस नंबर का उपयोग करें जब StringBuilder को तत्काल करना। StringBuilder sb = नया StringBuilder (estSize);

TextBox को अपडेट करते समय बस असाइनमेंट का उपयोग करें: textbox.text = sb.ToString ();

ऊपर के रूप में क्रॉस-थ्रेड संचालन के लिए देखें। हालाँकि BeginInvoke का उपयोग करें। UI अपडेट करते समय पृष्ठभूमि थ्रेड को ब्लॉक करने की आवश्यकता नहीं है।


1

ए) परिचय: पहले से ही उल्लेख किया गया है, उपयोग करें StringBuilder

बी) बिंदु: बहुत बार अद्यतन नहीं, यानी

DateTime dtLastUpdate = DateTime.MinValue;

while (condition)
{
    DoSomeWork();
    if (DateTime.Now - dtLastUpdate > TimeSpan.FromSeconds(2))
    {
        _form.Invoke(() => {textBox.Text = myStringBuilder.ToString()});
        dtLastUpdate = DateTime.Now;
    }
}

सी) यदि यह एक बार का काम है, तो 2 जीबी सीमा के भीतर रहने के लिए x64 आर्किटेक्चर का उपयोग करें।


1

StringBuilderमें ViewModel, स्ट्रिंग रिइंडिंग गड़बड़ी से बचेंगे और इसे बांधेंगे MyTextBox.Text। यह परिदृश्य कई बार प्रदर्शन बढ़ाएगा और मेमोरी उपयोग को कम करेगा।


0

जो कुछ उल्लेख नहीं किया गया है वह यह है कि भले ही आप बैकग्राउंड थ्रेड में ऑपरेशन कर रहे हों, यूआई एलिमेंट का अपडेट खुद मुख्य थ्रेड पर ही होना चाहिए (वैसे भी WinForms में)।

अपने टेक्स्टबॉक्स को अपडेट करते समय, क्या आपके पास ऐसा कोई कोड है जो दिखता है

if(textbox.dispatcher.checkAccess()){
    textbox.text += "whatever";
}else{
    textbox.dispatcher.invoke(...);
}

यदि ऐसा है, तो आपके बैकग्राउंड ऑप को निश्चित रूप से UI अपडेट द्वारा अड़चन दी जा रही है।

मैं सुझाव दूंगा कि आपका बैकग्राउंड स्टिंगब्यूबर्ल को ऊपर बताए अनुसार उपयोग करता है, लेकिन टेक्स्टबॉक्स को हर चक्र को अपडेट करने के बजाय, नियमित अंतराल पर इसे अपडेट करने की कोशिश करें कि क्या यह आपके लिए प्रदर्शन बढ़ाता है।

EDIT NOTE: WPF का उपयोग नहीं किया है।


0

आप कहते हैं कि मेमोरी तेजी से बढ़ती है। नहीं, यह एक द्विघात वृद्धि है , यानी एक बहुपद वृद्धि, जो एक घातीय वृद्धि के रूप में नाटकीय नहीं है।

आप निम्नलिखित मदों की संख्याओं को बनाए हुए हैं:

1 + 2 + 3 + 4 + 5 ... + n = (n^2 + n) /2.

के साथ n = 180,000आप के लिए कुल स्मृति आवंटन मिलता है 16,200,090,000 items, यानी 16.2 billion items! यह मेमोरी एक बार में आवंटित नहीं की जाएगी, लेकिन यह जीसी (कचरा संग्रहकर्ता) के लिए बहुत सारे सफाई का काम है!

यह भी ध्यान रखें, कि पिछले तार (जो बढ़ रहा है) को 179,999 बार नए स्ट्रिंग में कॉपी किया जाना चाहिए। कॉपी बाइट्स की कुल संख्या के साथ n^2ही चला जाता है !

जैसा कि दूसरों ने सुझाव दिया है, इसके बजाय एक लिस्टबॉक्स का उपयोग करें। यहां आप एक विशाल स्ट्रिंग बनाने के बिना नए तार जोड़ सकते हैं। A StringBuildमदद नहीं करता है, क्योंकि आप मध्यवर्ती परिणाम भी प्रदर्शित करना चाहते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.