2012-06-25 24 views
26

हलमेमोरी रिसाव जोखिम

वेब पर विरोधाभासी जानकारी का एक बहुत कुछ है, इस विषय के बारे में closures। @ जॉन के लिए धन्यवाद, मैं यह काम करने में कामयाब रहा कि बंद (जैसा कि नीचे उपयोग किया गया है) स्मृति रिसाव का कारण नहीं है, और यह भी आईई 8 में- वे लोग आम तौर पर दावा नहीं करते हैं। असल में मेरे कोड में केवल 1 रिसाव हुआ था, जो साबित करना मुश्किल नहीं था।

अब से, इस सवाल का मेरा उत्तर होगा:
AFAIK, केवल समय IE8 लीक, जब घटनाओं से जुड़े होते हैं/संचालकों वैश्विक वस्तु पर सेट कर रहे हैं है। (window.onload, window.onbeforeunload, ...)। इसके चारों ओर जाने के लिए, नीचे मेरा जवाब देखें।


विशाल अद्यतन:

मैं पूरी तरह से अब खो रहा हूँ ... कुछ समय के लेख और tuts पुरानी और नई दोनों के माध्यम से खुदाई के बाद, मैं के साथ कम से कम एक humongous छोड़ रहा हूँ अंतर्विरोध। जबकि में से एक जावास्क्रिप्ट गुरु की (डगलस Crockford) का कहना है:

के बाद से आईई अपना काम करने और चक्र को पुनः प्राप्त करने में असमर्थ है, यह हम पर गिर जाता है यह करने के लिए। यदि हम स्पष्ट रूप से चक्र तोड़ते हैं, तो IE स्मृति को पुनः प्राप्त करने में सक्षम होगा। माइक्रोसॉफ्ट के अनुसार, बंद स्मृति मेमोरी लीक का कारण हैं। यह निश्चित रूप से गहराई से गलत है, लेकिन यह माइक्रोसॉफ्ट की ओर जाता है कि माइक्रोसॉफ्ट की बग से निपटने के तरीके पर प्रोग्रामर को बहुत बुरी सलाह दी जाती है। यह पता चला है कि डीओएम पक्ष पर चक्र तोड़ना आसान है। जेस्क्रिप्ट पक्ष पर उन्हें तोड़ना लगभग असंभव है।

और @freakish ने बताया कि नीचे दिए गए मेरे स्निपेट jQuery की आंतरिक कार्यप्रणाली के समान हैं, मुझे अपने समाधान के बारे में बहुत सुरक्षित महसूस हुआ है जिससे स्मृति रिसाव नहीं हो रहा है। उसी समय मुझे this MSDN page मिला, जहां सेक्शन Circular References with Closures मेरे लिए विशेष रुचि थी। नीचे दिए गए चित्र काफी कैसे मेरे कोड काम करता है की एक योजनाबद्ध प्रतिनिधित्व है, यह नहीं है:

Circular References with Closures

फर्क सिर्फ इतना किया जा रहा है मैं तत्वों खुद को मेरी घटना श्रोताओं संलग्न नहीं की सामान्य ज्ञान है।
सभी एक ही डौगी काफी स्पष्ट है: बंद आईई में मेम-लीक का स्रोत नहीं है। यह विरोधाभास मुझे स्पष्ट करता है कि कौन सही है।

मुझे यह भी पता चला है कि रिसाव मुद्दा IE9 में पूरी तरह से हल नहीं किया गया है (लिंक एटीएम नहीं मिल सकता है)।

एक आखिरी बात: मैं भी जानने के लिए कि IE JScript इंजन के बाहर डोम, जो मुझे परेशान जब मैं एक <select> तत्व के बच्चों, एक ajax अनुरोध के आधार पर बदलने का एक स्थान में डालता प्रबंधन करता आए हैं :

function changeSeason(e) 
{ 
    var xhr,sendVal,targetID; 
    e = e || window.event;//(IE... 
    targetID = this.id.replace(/commonSourceFragment/,'commonTargetFragment');//fooHomeSelect -> barHomeSelect 
    sendVal = this.options[this.selectedIndex].innerHTML.trim().substring(0,1); 
    xhr = prepareAjax(false,(function(t) 
    { 
     return function() 
     { 
      reusableCallback.apply(this,[t]); 
     } 
    })(document.getElementById(targetID)),'/index/ajax'); 
    xhr({data:{newSelect:sendVal}}); 
} 

function reusableCallback(elem) 
{ 
    if (this.readyState === 4 && this.status === 200) 
    { 
     var data = JSON.parse(this.responseText); 
     elem.innerHTML = '<option>' + data.theArray.join('</option><option>') + '</option>'; 
    } 
} 

आईई वास्तव में डोम का प्रबंधन करता है जैसे कि JScript इंजन वहाँ नहीं थे, बाधाओं कि विकल्प तत्वों इस कोड का उपयोग पुनः आवंटित की जाती नहीं कर रहे हैं क्या हैं?
मैंने जानबूझकर इस स्निपेट को एक उदाहरण के रूप में जोड़ा है, क्योंकि इस मामले में मैं वैरिएबल को पास कर रहा हूं जो वैश्विक कार्य के लिए तर्क के रूप में क्लोजर स्कोप का हिस्सा हैं।मुझे इस अभ्यास पर कोई दस्तावेज नहीं मिला, लेकिन माइक्रोसॉफ्ट द्वारा प्रदान किए गए दस्तावेज के आधार पर, इसे किसी भी परिपत्र संदर्भ को तोड़ना चाहिए, है ना?



चेतावनी: लंबा सवाल ... (खेद)

मैं अजाक्स मेरी वेब अनुप्रयोग में कॉल करने के लिए काफी बड़ी JavaScripts की एक जोड़ी लिखा है। कई कॉलबैक और घटनाओं से बचने के लिए, मैं इवेंट प्रतिनिधिमंडल और बंदियों का पूरा फायदा उठा रहा हूं। अब मैंने एक ऐसा फ़ंक्शन लिखा है जिसमें मुझे संभावित मेमोरी लीक के रूप में सोच रहा है। हालांकि मुझे आईई> 8 सौदों को बंद करने के साथ 8 सौदों को बेहतर माना जाता है, फिर भी इसके पूर्ववर्ती, यह आईई 8 का समर्थन करने के लिए कंपनी नीति है।

नीचे मैंने जो कुछ भी किया है, उसका उदाहरण दिया है, here आप एक समान उदाहरण पा सकते हैं, हालांकि यह AJAX का उपयोग नहीं करता है, लेकिन एक सेटटाइमआउट, परिणाम काफी समान है। onreadystatechange कॉलबैक के अलावा

function prepareAjax(callback,method,url) 
{ 
    method = method || 'POST'; 
    callback = callback || success;//a default CB, just logs/alerts the response 
    url = url || getUrl();//makes default url /currentController/ajax 
    var xhr = createXHRObject();//try{}catch etc... 
    xhr.open(method,url,true); 
    xhr.setRequestMethod('X-Requested-with','XMLHttpRequest'); 
    xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded'); 
    xhr.setRequestHeader('Accept','*/*'); 
    xhr.onreadystatechange = function() 
    { 
     callback.apply(xhr); 
    } 
    return function(data) 
    { 
     //do some checks on data before sending: data.hasOwnProperty('user') etc... 
     xhr.send(data); 
    } 
} 

सभी सुंदर सीधी-सपाट सामान,: (आप निश्चित रूप से नीचे दिए गए कोड को छोड़ सकते हैं सवाल ही,)

कोड मैं मन में है यह है। मैंने आईई के साथ कुछ मुद्दों को देखा जब सीधे हैंडलर बाध्यकारी: xhr.onreadystatechange = callback;, इसलिए अनाम कार्य। पता नहीं क्यों, लेकिन मैंने इसे काम करने का सबसे आसान तरीका पाया।

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

function handleClick(e) 
{ 
    var target,parent,data,i; 
    e = e || window.event; 
    target = e.target || e.srcElement; 
    if (target.tagName.toLowerCase() !== 'input' && target.className !== 'delegateMe') 
    { 
     return true; 
    } 
    parent = target; 
    while(parent.tagName.toLowerCase() !== 'tr') 
    { 
     parent = parent.parentNode; 
    } 
    data = {}; 
    for(i=0;i<parent.cells;i++) 
    { 
     data[parent.cells[i].className] = parent.cells[i].innerHTML; 
    } 
    //data looks something like {name:'Bar',firstName:'Foo',title:'Mr.'} 
    i = prepareAjax((function(t) 
    { 
     return function() 
     { 
      if (this.readyState === 4 && this.status === 200) 
      { 
       //check responseText and, if ok: 
       t.setAttribute('disabled','disabled'); 
      } 
     } 
    })(target)); 
    i(data); 
} 

आप देख सकते हैं, onreadystatechange कॉलबैक एक समारोह, कि target तत्व के संदर्भ में जब कॉलबैक कहा जाता है प्रदान करता है के रिटर्न मान है: तो मैं कुछ ईवेंट हैंडलर्स कि इस तरह दिखना है। घटना प्रतिनिधिमंडल के लिए धन्यवाद, मुझे उन घटनाओं के बारे में चिंता करने की ज़रूरत नहीं है जो उस तत्व से बंधे रहें, जब मैं इसे डीओएम से हटाने का फैसला करता हूं (जिसे मैं कभी-कभी करता हूं)।
मेरे मन के लिए, तथापि, कॉलबैक फ़ंक्शन का कॉल वस्तु बहुत ज्यादा IE के JScript इंजन और उसके कचरा कलेक्टर के लिए साबित हो सकता:

घटना ==> हैंडलर ==> prepareAjax एक बहुत साधारण कॉल अनुक्रम है, लेकिन कॉलबैक तर्क:

[anon। func (तर्क टी = लक्ष्य) एनन लौटाता है। एफ prepareAjax करने के लिए एक निजी चर बारी में,]
      ===> एक anon कॉलबैक फ़ंक्शन पर पारित किया है, XHR वस्तु के लिए विधि .apply का उपयोग कर कहा जाता है (जो बारी refs में वापस लक्षित करने के लिए टी की पहुंच है) फ़ंक्शन

मैंने एफएफ और क्रोम में इस "निर्माण" का परीक्षण किया है। यह ठीक काम करता है, लेकिन बंद होने पर बंद होने पर बंद होने का इस तरह का कॉलस्टैक, प्रत्येक अवसर पर डीओएम तत्व के संदर्भ में गुजरने वाले प्रत्येक अवसर पर IE (विशेष रूप से IE9 से पहले संस्करण) में कोई समस्या हो सकती है?


नहीं, मैं jQuery या अन्य libs का उपयोग नहीं कर रहा हूं। मुझे शुद्ध जेएस पसंद है, और मैं इस गंभीर रूप से अंतर्निहित भाषा के बारे में जितना चाहूं उतना जानना चाहता हूं।कोड स्निपेट वास्तविक प्रति-पेस्ट उदाहरण नहीं हैं, लेकिन आईएमओ प्रदान करते हैं, मैं एक अच्छी प्रतिनिधित्व करता हूं कि मैं अपनी स्क्रिप्ट में प्रतिनिधिमंडल, बंद और कॉलबैक का उपयोग कैसे कर रहा हूं। तो अगर कुछ वाक्यविन्यास बिल्कुल सही नहीं है, तो इसे सही करने के लिए स्वतंत्र महसूस करें, लेकिन यह सवाल यह नहीं है कि यह सवाल निश्चित रूप से है।

+0

मैं क्यों 'xhr.onreadystatechange = callback' आप के लिए काम नहीं करता है पता नहीं है: मामले में किसी को परवाह नहीं है, यहाँ ucFirst स्ट्रिंग विधि है। दिलचस्प। लेकिन वास्तव में इस हिस्से को कोई फर्क नहीं पड़ता, स्मृति रिसाव सबसे दिलचस्प बात है। मैंने कभी ऐसा व्यवहार नहीं देखा है, हालांकि मैंने थोड़ी देर के लिए IE <9 के साथ काम नहीं किया है। – freakish

+2

बीटीडब्ल्यू: आपको jQuery का उपयोग करने की आवश्यकता नहीं है, लेकिन आप इसके स्रोत कोड को देख सकते हैं। :) मैंने किया और ऐसा लगता है कि वे कुछ अलग नहीं कर रहे हैं तो आप हैं। यह स्मृति रिसाव नहीं देना चाहिए। क्या आप निश्चित हैं कि आप कहीं भी 'xhr' का संदर्भ नहीं रखते हैं? या 'तैयार AJAX' परिणाम के लिए? – freakish

+0

@freakish: मुझे उम्मीद नहीं है कि 'xhr.onreadystatechange = callback' स्मृति समस्याओं (यदि कोई हो) का कारण हो। मैंने इसका उल्लेख किया क्योंकि मैं भी, आईई के हिस्से पर यह अजीब व्यवहार पाता हूं। लेकिन मैंने लंबे समय से आईई की तार्किक व्यवहार की आशा छोड़ दी है। आपके अनुसार, क्या यह कोड स्मृति समस्याओं का कारण बन सकता है, यही वह है जो मुझे –

उत्तर

23

मैं गैर-ब्राउज़र EcmaScript (त्रुटि .. JScr ... जावास्क्रिप्ट) प्रोजेक्ट पर माइक्रोसॉफ्ट के भीतर जावास्क्रिप्ट के लिए पूर्व-प्रोग्राम प्रबंधक के साथ काम करता था। बंद होने के बारे में हमने कुछ लंबी चर्चा की थी। अंत में, मुद्दा यह है कि वे जीसी के लिए कठिन हैं, असंभव नहीं। मुझे डीसी की चर्चा को पढ़ना होगा कि एमएस कितना गलत है कि बंद होने के कारण मेमोरी लीक का कारण बनता है - आईई के पुराने कार्यान्वयन के लिए, बंद निश्चित रूप से समस्याग्रस्त थे, क्योंकि एमएस कार्यान्वयन के साथ संग्रहित करने के लिए वे कचरे के लिए बहुत कठिन थे। मुझे यह अजीब लगता है कि एक याहू लड़का एमएस आर्किटेक्ट्स को बताने की कोशिश करेगा कि उनके कोड के साथ एक ज्ञात मुद्दा कहीं और था। जितना मैं अपने काम की सराहना करता हूं, मैं नहीं देखता कि उसके पास क्या आधार था।

याद रखें, ऊपर उल्लिखित लेख आईई 6 को संदर्भित करता है, क्योंकि आईई 7 अभी भी इसके लेखन के समय भारी विकास में था।

एक तरफ के रूप में - भगवान का शुक्र है, आईई 6 मर चुका है (अंतिम संस्कार चित्रों को खोदना नहीं है)। हालांकि, इसकी विरासत को मत भूलना ... मैंने अभी तक किसी को यह विश्वसनीय तर्क नहीं दिया है कि यह रिलीज के दिन 1 पर दुनिया का सबसे अच्छा ब्राउज़र नहीं था - समस्या यह थी कि उन्होंने ब्राउज़र युद्ध जीता । और इसलिए, उनके इतिहास के बड़े गलतियों में से एक के लिए कितनी मात्रा में - उन्होंने टीम को बाद में निकाल दिया और यह लगभग 5 वर्षों तक स्थिर रहा। आईई टीम 4 या 5 लोगों के लिए साल के लिए बग फिक्स कर रही थी, एक विशाल मस्तिष्क नाली बना रही थी और नाटकीय रूप से वक्र के पीछे गिर रही थी। जब तक उन्होंने टीम को फिर से साझा किया और महसूस किया कि वे कहां थे, वे एक मोनोलॉथिक कोडबेस से निपटने के अतिरिक्त क्रूर होने के कारण वर्षों से पीछे थे, जिसे कोई भी वास्तव में समझ में नहीं आया था। यह कंपनी में एक आंतरिक के रूप में मेरा परिप्रेक्ष्य है, लेकिन सीधे उस टीम से बंधे नहीं है।

भी याद रखें, आईई ने कभी भी बंद होने के लिए अनुकूलित नहीं किया क्योंकि कोई प्रोटोटाइपजेएस नहीं था (हेक, कोई रेल नहीं था), और jQuery मुश्किल से रेसिग के सिर में एक चमकदार था।

लेखन के समय, वे अभी भी 256 मेगाहर्ट्ज रैम वाली मशीनों को लक्षित कर रहे थे, जो कि जीवन के अंत में भी नहीं थे।

मैंने सोचा कि यह आपको एक प्रश्न के पूरे पुस्तक को पढ़ने के बाद, आपको इस इतिहास के पाठ को सौंपने के लिए उचित है।

अंत में, मेरा मुद्दा यह है कि आप सामग्री का संदर्भ दे रहे हैं जो बेहद दिनांकित है। हां, आईई 6 में बंद होने से बचें, क्योंकि वे स्मृति रिसाव का कारण बनते हैं - लेकिन आईई 6 में क्या नहीं था?

अंत में, यह एक समस्या है जिसे एमएस ने संबोधित किया है और संबोधित करना जारी रखा है। आप कुछ स्तरों को बंद करने जा रहे हैं, यह मामला भी था, फिर भी वापस।

मुझे पता है कि उन्होंने आईई 8 के आसपास इस क्षेत्र में भारी काम किया है (क्योंकि मेरी अनुपयोगी परियोजना गैर-समय-समय पर मानक जावास्क्रिप्ट इंजन का उपयोग करती है), और यह काम आईई 9/10 में जारी रहा है। स्टेटकॉन्टर (http://gs.statcounter.com/) से पता चलता है कि आईई 7 एक साल पहले 6% से नीचे 1.5% बाजार हिस्सेदारी नीचे है, और 'नई' साइटों को विकसित करने में, आई 7 कम और कम प्रासंगिक हो जाता है। आप NetScape 2.0 के लिए भी विकसित कर सकते हैं, जिसने जावास्क्रिप्ट समर्थन प्रस्तुत किया है, लेकिन यह केवल थोड़ा कम मूर्ख होगा।

वास्तव में ... किसी इंजन के लिए अधिक अनुकूलन करने की कोशिश न करें जो अब मौजूद नहीं है।

+1

आपके उत्तर पढ़ने के दौरान कई चकल्स के लिए +1। मेरे पास पुराने इंजनों के लिए अधिक अनुकूलन का कोई इरादा नहीं है। सच है, डीसी लिंक बहुत पुराना है। लेकिन ईमानदारी से, स्मृति रिसाव अक्सर बात की जाती है, लेकिन यह अविश्वसनीय रूप से कठिन है कि उन्हें क्या कारण है, क्यों और कहाँ। एमएसडीएन लिंक मैंने डीओएम ऑब्जेक्ट्स के परिपत्र संदर्भों के बारे में बात की है, जो कि मैं जो कर रहा हूं उसके बहुत करीब आता है, जो कि डीओएम तत्वों के प्रबंधन पर कोई वास्तविक नियंत्रण नहीं रखता है, और मुझे उत्सुक/घबराहट मिलती है। मैं एक _definitive _ उत्तर की तलाश में हूं: "मेम मुद्दे, क्या ब्राउज़र, क्यों और कैसे उनसे बचने का कारण बनता है?" –

+0

संक्षिप्त उत्तर फ़्लैश है। लंबे उत्तर का जवाब आपने सिर पर मारा है ... आपके पास कचरा संग्रह तक पहुंच नहीं है। मेमोरी प्रबंधन वास्तव में ब्राउज़र के विक्रेता पर निर्भर करता है, और वास्तव में एक बंद बॉक्स होगा जब तक कि आपके पास स्रोत कोड तक पहुंच न हो (और पता लगाने के लिए तैयार हैं), क्योंकि व्यक्तिगत ब्राउज़र वास्तव में आपको यह एक्सेस नहीं देते हैं। एमएसडीएन आलेख में परिपत्र संदर्भ एक पूर्व कार्यान्वयन को दर्शाता है कि आईई वस्तुओं के साथ कैसे काम करता है (मैं आईई कहता हूं, क्योंकि यह डीओएम + स्क्रिप्ट का संयोजन है), और मुझे संदेह होगा कि यह आज सच है। –

+1

किसी भी निश्चित उत्तर को पुराना हो जाएगा जैसे ही डेवलपर्स को पता चलता है कि कोई समस्या है और इसे ठीक करने के लिए समय निकालें। इसके अलावा, नई विशेषताएं नई लीक (कैनवास) जोड़ देंगे। निश्चित रूप से ऐसी चीजें हैं जो हमेशा स्मृति रिसाव का कारण बनती हैं (उदाहरण के लिए, ग्लोबल स्कोप जेएस में लगातार चीजें या डीओएम को प्रस्तुत वस्तुओं का एक गुच्छा जोड़ना)। ऐसा कहा जा रहा है कि सवाल एक निश्चित जवाब नहीं है, मुझे डर है। –

4

ठीक है, कि IEJSLeaksDetector उपकरण के साथ कुछ समय बिताने के बाद, मुझे पता चला है कि चीजों को मैं अपने मूल प्रश्न में के बारे में बात का कारण नहीं है सदस्य लीक। हालांकि, वहां 1 रिसाव था जो पॉप अप हुआ था।शुक्र है, मैं एक समाधान खोजने के लिए प्रबंधित किया है:

मैं एक मुख्य स्क्रिप्ट है, और नीचे, वहाँ एक पुराने स्कूल है:

window.onload = function() 
{ 
    //do some stuff, get URI, and call: 
    this['_init' + uri[0].ucFirst()](uri);//calls func like _initController 
    //ucFirst is an augmentation of the String.prototype 
} 

यह IE8 में एक रिसाव का कारण बनता है, यही कारण है कि मैं नहीं कर सकता window.onbeforeunload हैंडलर के साथ ठीक करें। ऐसा लगता है कि आपको हैंडलर को वैश्विक वस्तु पर बाध्य करने से बचाना है। समाधान बंद और घटना श्रोताओं में निहित है, यह एक faff का एक सा है, लेकिन यहां मैं क्या कर समाप्त हो गया है:

(function(go) 
{//use closure to avoid leaking issue in IE 
    function loader() 
    { 
     var uri = location.href.split(location.host)[1].split('/'); 
     //do stuff 
     if (this['_init' + uri[0].ucFirst()] instanceof Function) 
     { 
      this['_init' + uri[0].ucFirst()](uri); 
     } 
     if (!(this.removeEventListener)) 
     { 
      this.detachEvent('onload',loader);//(fix leak? 
      return null; 
     } 
     this.removeEventListener('load',loader,false); 
    } 
    if (!(go.addEventListener)) 
    { 
     go.attachEvent('onload',loader);//(IE... 
    } 
    else 
    { 
     go.addEventListener('load',loader,false); 
    } 
})(window); 

इस तरह, (पर) लोड घटना सिर्फ window.load हैंडलर रिटर्न से पहले अबाध है, के अनुसार IEJSLeaksDetector टूल में, नहीं मेरे आवेदन में लीक हैं। मैं उससे खुश हूं। मुझे उम्मीद है कि यह स्निपेट आप में से किसी के लिए कुछ उपयोग है - अगर किसी के पास इस दृष्टिकोण को बेहतर बनाने के सुझाव हैं, तो ऐसा करने में संकोच न करें!

चीयर्स, और आप सभी को धन्यवाद जो पढ़ने और मेरी ड्रबबल को पढ़ने की परेशानी से गुजर चुके हैं!


पुनश्च:

if (!(String.prototype.ucFirst)) 
{ 
    String.prototype.ucFirst = function() 
    { 
     "use strict"; 
     return this.charAt(0).toUpperCase() + this.slice(1); 
    }; 
} 
+0

आईई 8 के लिए IEJSLeaksDetector उपयोगी है, या केवल आईई 8 से पहले संस्करणों के लिए? – Notre

+0

यह आईई 8 के लिए उपयोगी है, जिसे आईई 8 के साथ विकसित किया गया था (और आईई 8 जारी होने पर सक्रिय विकास के तहत भी) \ –

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