Chrome के साथ जावास्क्रिप्ट मेमोरी लीक खोजना


163

मैंने एक बहुत ही सरल परीक्षण मामला बनाया है जो एक बैकबोन दृश्य बनाता है, एक हैंडलर को एक घटना में संलग्न करता है, और एक उपयोगकर्ता-परिभाषित वर्ग को त्वरित करता है। मेरा मानना ​​है कि इस नमूने में "निकालें" बटन पर क्लिक करने से, सब कुछ साफ हो जाएगा और मेमोरी लीक नहीं होनी चाहिए।

कोड के लिए एक jsfiddle यहां है: http://jsfiddle.net/4QhR2/

// scope everything to a function
function main() {

    function MyWrapper() {
        this.element = null;
    }
    MyWrapper.prototype.set = function(elem) {
        this.element = elem;
    }
    MyWrapper.prototype.get = function() {
        return this.element;
    }

    var MyView = Backbone.View.extend({
        tagName : "div",
        id : "view",
        events : {
            "click #button" : "onButton",
        },    
        initialize : function(options) {        
            // done for demo purposes only, should be using templates
            this.html_text = "<input type='text' id='textbox' /><button id='button'>Remove</button>";        
            this.listenTo(this,"all",function(){console.log("Event: "+arguments[0]);});
        },
        render : function() {        
            this.$el.html(this.html_text);

            this.wrapper = new MyWrapper();
            this.wrapper.set(this.$("#textbox"));
            this.wrapper.get().val("placeholder");

            return this;
        },
        onButton : function() {
            // assume this gets .remove() called on subviews (if they existed)
            this.trigger("cleanup");
            this.remove();
        }
    });

    var view = new MyView();
    $("#content").append(view.render().el);
}

main();

हालाँकि, मैं स्पष्ट नहीं हूँ कि यह सत्यापित करने के लिए कि यह वास्तव में मामला है, Google Chrome के प्रोफाइलर का उपयोग कैसे किया जाए। एक गज़िलियन चीजें हैं जो ढेर प्रोफाइलर स्नैपशॉट पर दिखाई देती हैं, और मुझे नहीं पता कि अच्छा / बुरा क्या है, इसे कैसे डिकोड किया जाए। मैंने अभी तक इस पर जो ट्यूटोरियल देखे हैं, वे या तो मुझे "स्नैपशॉट प्रोफाइलर का उपयोग करने" के लिए कहेंगे या मुझे पूरे प्रोफिलर के काम करने के तरीके के बारे में विस्तृत रूप से बताएंगे। क्या केवल प्रोफाइलर को एक उपकरण के रूप में उपयोग करना संभव है, या क्या मुझे वास्तव में यह समझना है कि पूरी चीज को कैसे इंजीनियर किया गया था?

संपादित करें: इन जैसे ट्यूटोरियल:

जीमेल मेमोरी लीक फिक्सिंग

DevTools का उपयोग करना

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

तो आपको एक रिसाव मिला। अब क्या?

  • प्रोफाइल पैनल के निचले आधे हिस्से में लीक हुए ऑब्जेक्ट के रिटेनिंग पथ की जांच करें

  • यदि आवंटन साइट को आसानी से अनुमान नहीं लगाया जा सकता है (यानी घटना श्रोता):

  • आवंटन के लिए स्टैक ट्रेस को बचाने के लिए जेएस कंसोल के माध्यम से रिटेनिंग ऑब्जेक्ट के निर्माता को साधन दें

  • क्लोजर का उपयोग करना? निर्माण के दौरान निर्माण संपत्ति को सेट करने के लिए उपयुक्त मौजूदा ध्वज (जैसे goog.events.Listener.ENABLE_MONITORING) सक्षम करें

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

इन कुछ और विशिष्ट मुद्दों को @ जोनाथन नागुइन के जवाब में नीचे उठाया गया है ।


2
मुझे ब्राउज़रों में मेमोरी उपयोग के परीक्षण के बारे में कुछ भी नहीं पता है, लेकिन अगर आपने इसे नहीं देखा है, तो Chrome वेब निरीक्षक के बारे में Addy Osmani का लेख सहायक हो सकता है।
पॉल डी। वेट

1
सुझाव के लिए धन्यवाद, पॉल। हालाँकि, जब मैं क्लिक करने से पहले एक स्नैपशॉट लेता हूं, और उसके बाद एक और क्लिक किया जाता है, और फिर 'स्नैपशॉट 1 और 2 के बीच आवंटित ऑब्जेक्ट्स' का चयन करें (जैसा कि उनके लेख में सुझाव दिया गया है) अभी भी 2000 से अधिक ऑब्जेक्ट मौजूद हैं। उदाहरण के लिए, 4 'HTMLButtonElement' प्रविष्टियाँ हैं, जो मेरे लिए कोई मायने नहीं रखती हैं। सच में, मुझे नहीं पता कि क्या चल रहा है।
इलेवनटाइन 18

3
दोह, कि विशेष रूप से उपयोगी नहीं लगता है। यह हो सकता है कि जावास्क्रिप्ट जैसी कचरा-एकत्रित भाषा के साथ, आप वास्तव में यह सत्यापित करने के लिए नहीं हैं कि आप अपने परीक्षण के रूप में एक स्तर पर स्मृति के साथ क्या कर रहे हैं। मेमोरी लीक की जांच करने का एक बेहतर तरीका हो सकता है mainकि आप एक बार के बजाय 10,000 बार कॉल करें , और देखें कि क्या आप अंत में उपयोग में बहुत अधिक मेमोरी के साथ समाप्त होते हैं।
पॉल डी। वेट

3
@ PaulD.Waite हाँ, शायद। लेकिन यह मुझे लगता है कि मुझे अभी भी यह निर्धारित करने के लिए एक दानेदार स्तर के विश्लेषण की आवश्यकता है कि मुद्दा क्या है, बजाय यह कहने (या कहने के) सक्षम नहीं होने के बजाय: "ठीक है, यहां एक स्मृति समस्या है"। और मुझे यह आभास हो जाता है कि मुझे उनके प्रोफाइलर का उपयोग इस तरह के दानेदार स्तर पर करने में सक्षम होना चाहिए ... मुझे यकीन नहीं है कि कैसे :(
EleventyOne

जवाबों:


205

मेमोरी लीक खोजने के लिए एक अच्छा वर्कफ़्लो तीन स्नैपशॉट तकनीक है, जिसका इस्तेमाल पहले लोरेना ली और जीमेल टीम ने अपनी कुछ मेमोरी समस्याओं को हल करने के लिए किया था। चरण सामान्य रूप से हैं:

  • एक ढेर स्नैपशॉट लें।
  • कार्य करना।
  • एक और ढेर स्नैपशॉट लें।
  • एक ही सामान को दोहराएं।
  • एक और ढेर स्नैपशॉट लें।
  • स्नैपशॉट 3 के "सारांश" दृश्य में स्नैपशॉट 1 और 2 के बीच आवंटित वस्तुओं को फ़िल्टर करें।

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

  • HTML चलाएं (स्थानीय रूप से इस पते के उपयोग से बचाया ) और स्नैपशॉट लें।
  • दृश्य बनाने के लिए प्रारंभ पर क्लिक करें।
  • दूसरा स्नैपशॉट लें।
  • निकालें पर क्लिक करें।
  • दूसरा स्नैपशॉट लें।
  • स्नैपशॉट 3 के "सारांश" दृश्य में स्नैपशॉट 1 और 2 के बीच आवंटित वस्तुओं को फ़िल्टर करें।

अब आप स्मृति लीक खोजने के लिए तैयार हैं!

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

यहां छवि विवरण दर्ज करें

हालाँकि, पीले नोड्स का जावास्क्रिप्ट से सीधा संदर्भ होता है। अपने जावास्क्रिप्ट से संदर्भ खोजने के लिए एक ही अलग डोम के पेड़ में पीले रंग के नोड्स देखें। DOM विंडो से एलिमेंट तक जाने वाली प्रॉपर्टीज की एक चेन होनी चाहिए।

अपने विशेष में आप लाल रंग के रूप में चिह्नित HTML Div तत्व देख सकते हैं। यदि आप उस तत्व का विस्तार करते हैं जो आप देखेंगे कि "कैश" फ़ंक्शन द्वारा संदर्भित किया गया है।

यहां छवि विवरण दर्ज करें

पंक्ति का चयन करें और अपने कंसोल प्रकार $ 0 में, आपको वास्तविक फ़ंक्शन और स्थान दिखाई देगा:

>$0
function cache( key, value ) {
        // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
        if ( keys.push( key += " " ) > Expr.cacheLength ) {
            // Only keep the most recent entries
            delete cache[ keys.shift() ];
        }
        return (cache[ key ] = value);
    }                                                     jquery-2.0.2.js:1166

यह वह जगह है जहाँ आपके तत्व को संदर्भित किया जा रहा है। दुर्भाग्य से वहाँ बहुत कुछ आप कर सकते हैं नहीं है, यह jQuery से एक आंतरिक तंत्र है। लेकिन, सिर्फ परीक्षण उद्देश्य के लिए, फ़ंक्शन पर जाएं और विधि को इसमें बदल दें:

function cache( key, value ) {
    return value;
}

अब यदि आप इस प्रक्रिया को दोहराते हैं तो आपको कोई लाल नोड दिखाई नहीं देगा :)

प्रलेखन:


8
मैं आपके प्रयास की सराहना करता हूँ। दरअसल, ट्यूटोरियल में नियमित रूप से तीन स्नैपशॉट तकनीक का उल्लेख किया गया है। दुर्भाग्य से, विवरण अक्सर बाहर छोड़ दिया जाता है। उदाहरण के लिए, मैं $0कंसोल में फ़ंक्शन की शुरूआत की सराहना करता हूं , जो मेरे लिए नया था - निश्चित रूप से, मुझे नहीं पता कि यह क्या कर रहा है या आप इसे कैसे उपयोग करना जानते हैं ( $1ऐसा $2ही काम करते समय बेकार लगता है)। दूसरी बात, आप पंक्ति को उजागर करना कैसे जानते #button in function cache()थे और अन्य दर्जनों पंक्तियों में से कोई नहीं? अंत में, NodeListऔर HTMLInputElementभी लाल नोड्स हैं, लेकिन मैं उन्हें समझ नहीं सकता।
ग्यारहवें दिन

7
आपको कैसे पता चला कि cacheपंक्ति में जानकारी थी जबकि अन्य नहीं थी? ऐसी कई शाखाएँ हैं जिनकी दूरी कम है जो cacheएक है। और मुझे यकीन नहीं है कि आप कैसे जानते हैं कि HTMLInputElementयह एक बच्चा है HTMLDivElement। मैं इसे इसके अंदर संदर्भित देखता हूं ("HTMLDivElement में मूलनिवासी"), लेकिन यह खुद को और दो HTMLButtonElementएस को भी संदर्भित करता है, जो मेरे लिए कोई मतलब नहीं है। मैं निश्चित रूप से इस उदाहरण के उत्तर की पहचान करने के लिए आपकी सराहना करता हूं, लेकिन मुझे वास्तव में यह पता नहीं होगा कि इसे अन्य मुद्दों पर कैसे सामान्य किया जाए।
ग्यारहवें दिन

2
यह अजीब है, मैं आपके उदाहरण का उपयोग कर रहा था और मुझे आपके स्क्रीनशॉट से (आपके स्क्रीनशॉट से) अलग परिणाम मिला। फिर भी, मैं आपकी सभी मदद की सराहना करता हूं। मुझे लगता है कि मेरे पास अभी के लिए पर्याप्त है, और जब मेरे पास वास्तविक जीवन का उदाहरण है जिसे मुझे विशिष्ट सहायता की आवश्यकता है, तो मैं यहां एक नया प्रश्न बनाऊंगा। एक बार फिर धन्यवाद।
इलेवनटॉवन

2
$ 0 के लिए स्पष्टीकरण यहाँ पाया जा सकता है: developer.chrome.com/devtools/docs/commandline-api#0-4
Sukrit Gupta

4
क्या Filter objects allocated between Snapshots 1 and 2 in Snapshot 3's "Summary" view.मतलब है?
के - एसओ में विषाक्तता बढ़ रही है।

8

यहाँ jsfiddle की मेमोरी प्रोफाइलिंग पर एक टिप दी गई है: अपने jsfiddle परिणाम को अलग करने के लिए निम्न URL का उपयोग करें, यह jsfiddle ढांचे के सभी को हटा देता है और केवल आपका परिणाम लोड करता है।

http://jsfiddle.net/4QhR2/show/

मैं कभी भी यह पता लगाने में सक्षम नहीं था कि मेमोरी लीक को ट्रैक करने के लिए टाइमलाइन और प्रोइलर का उपयोग कैसे किया जाए, जब तक कि मैं निम्नलिखित दस्तावेज नहीं पढ़ता। 'ऑब्जेक्ट एलोकेशन ट्रैकर' शीर्षक वाले सेक्शन को पढ़ने के बाद मैं 'रिकॉर्ड हीप एलोकेशन' टूल का उपयोग करने में सक्षम था, और कुछ डिटैक्ड डोम नोड्स को ट्रैक कर सकता था।

मैंने बैकबोन ईवेंट प्रतिनिधिमंडल का उपयोग करके jQuery इवेंट बाइंडिंग से स्विच करके समस्या को ठीक किया। यह मेरी समझ है कि यदि आप कॉल करते हैं तो बैकबोन के नए संस्करण स्वचालित रूप से आपके लिए घटनाओं को खोल देंगे View.remove()। अपने आप में से कुछ डेमो को निष्पादित करें, वे आपको पहचानने के लिए मेमोरी लीक के साथ सेट किए गए हैं। यदि आप इस दस्तावेज़ का अध्ययन करने के बाद भी इसे प्राप्त नहीं करते हैं तो यहां बेझिझक प्रश्न पूछें।

https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling


6

मूल रूप से आपको अपने ढेर स्नैपशॉट के अंदर वस्तुओं की संख्या को देखने की आवश्यकता है। यदि दो स्नैपशॉट के बीच ऑब्जेक्ट की संख्या बढ़ जाती है और आपने ऑब्जेक्ट का निपटान कर दिया है, तो आपके पास मेमोरी लीक है। मेरी सलाह है कि अपने कोड में ईवेंट हैंडलर की तलाश करें जो अलग न हों।


3
उदाहरण के लिए, यदि मैं jsfiddle के ढेर स्नैपशॉट को देखता हूं, तो इससे पहले कि मैं 'निकालें' पर क्लिक करता हूं, वहाँ 100,000 से अधिक ऑब्जेक्ट मौजूद हैं। मैं उन वस्तुओं की तलाश कहाँ करूँगा जो मेरे jsfiddle के कोड ने वास्तव में बनाई हैं? मैंने सोचा कि Window/http://jsfiddle.net/4QhR2/showयह उपयोगी हो सकता है, लेकिन यह सिर्फ अंतहीन कार्य है। मुझे नहीं पता कि वहां क्या चल रहा है।
ग्यारहवें

@EleventyOne: मैं jsFiddle का उपयोग नहीं करूंगा। सिर्फ परीक्षण के लिए अपने कंप्यूटर पर एक फ़ाइल क्यों नहीं बनाएं?
ब्लू स्काईज

1
@BlueSkies मैंने एक jsfiddle बनाया ताकि यहां के लोग एक ही कोडबेस से काम कर सकें। फिर भी, जब मैं परीक्षण के लिए अपने कंप्यूटर पर एक फ़ाइल बनाता हूं, तो ढेर स्नैपशॉट में मौजूद 50,000+ ऑब्जेक्ट्स अभी भी हैं।
इलेवनटाइन

@EleventyOne वन हीप स्नैपशॉट आपको यह अनुमान नहीं देता है कि आपके पास मेमोरी लीक है या नहीं। आपको कम से कम दो की जरूरत है।
कोनस्टेंटिन दिनेव

2
वास्तव में। मैं इस बात पर प्रकाश डाल रहा था कि यह जानना कितना मुश्किल है कि जब हजारों वस्तुएं मौजूद हों तो क्या देखना चाहिए।
इलेवनटॉवन


3

आप डेवलपर टूल में टाइमलाइन टैब भी देख सकते हैं। अपने ऐप के उपयोग को रिकॉर्ड करें और DOM नोड और इवेंट श्रोता गणना पर नज़र रखें।

यदि मेमोरी ग्राफ़ वास्तव में मेमोरी लीक का संकेत देगा, तो आप प्रोफाइलर का उपयोग करके यह पता लगा सकते हैं कि लीक क्या है।


3

आप भी पढ़ना चाहेंगे:

http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/

यह क्रोम डेवलपर टूल के उपयोग की व्याख्या करता है और हीप स्नैपशॉट तुलना और उपलब्ध विभिन्न हेप स्नैपशॉट विचारों का उपयोग करके मेमोरी लीक की पुष्टि और पता लगाने के तरीके के बारे में कुछ कदम-दर-चरण सलाह देता है।


2

मैं एक ढेर स्नैपशॉट लेने की सलाह देता हूं, वे मेमोरी लीक का पता लगाने के लिए उत्कृष्ट हैं, क्रोम स्नैपशॉट का एक उत्कृष्ट काम करता है।

अपनी डिग्री के लिए मेरी शोध परियोजना में मैं एक इंटरेक्टिव वेब एप्लिकेशन का निर्माण कर रहा था जिसमें 'लेयर्स' में निर्मित बहुत सारा डेटा उत्पन्न करना था, इनमें से कई लेयर यूआई में 'डिलीट' हो जाएंगे लेकिन किसी कारण से मेमोरी नहीं हुई निपटाया जा रहा है, स्नैपशॉट टूल का उपयोग करके मैं यह निर्धारित करने में सक्षम था कि JQuery ऑब्जेक्ट पर एक संदर्भ रख रहा था (स्रोत तब था जब मैं एक .load()घटना को ट्रिगर करने की कोशिश कर रहा था जो कि दायरे से बाहर जाने के बावजूद संदर्भ रखा गया था)। यह जानकारी हाथ में आने के बाद मेरी परियोजना बच गई, यह एक बहुत ही उपयोगी उपकरण है जब आप अन्य लोगों के पुस्तकालयों का उपयोग कर रहे हैं और आपके पास जीसी को अपना काम करने से रोकने वाले संदर्भों का मुद्दा है।

संपादित करें: यह आगे योजना बनाने के लिए भी उपयोगी है कि आप स्नैपशॉटिंग में बिताए समय को कम करने के लिए क्या कार्रवाई करने जा रहे हैं, इस समस्या का कारण बन सकते हैं और प्रत्येक परिदृश्य का परीक्षण कर सकते हैं, इससे पहले और बाद में स्नैपशॉट बना सकते हैं।


0

Chrome डेवलपर टूल का उपयोग करके मेमोरी लीक की पहचान करने के संबंध में कुछ महत्वपूर्ण नोट्स:

1) क्रोम में पासवर्ड और नंबर फ़ील्ड जैसे कुछ तत्वों के लिए मेमोरी लीक है। https://bugs.chromium.org/p/chromium/issues/detail?id=967438 । डिसैगिंग तत्वों की खोज करते समय वे आपके ढेर स्नैपशॉट को विचलित करते हुए उन का उपयोग करने से बचें।

2) ब्राउज़र कंसोल में कुछ भी लॉग इन करने से बचें । Chrome कंसोल में लिखी गई वस्तुओं को इकट्ठा नहीं करेगा, इसलिए आपके परिणाम को प्रभावित करेगा। आप स्क्रिप्ट / पृष्ठ की शुरुआत में निम्नलिखित कोड रखकर आउटपुट को दबा सकते हैं:

console.log = function() {};
console.warn = console.log;
console.error = console.log;

3) ढेर स्नैपशॉट का उपयोग करें और अलग किए गए DOM तत्वों की पहचान करने के लिए "अलग करें" खोजें। वस्तुओं को मँडराकर, आपको आईडी और बाहरी HTML सहित सभी गुणों तक पहुँच मिलती है जो प्रत्येक तत्व को पहचानने में मदद कर सकते हैं। जेएस हीप स्नैपशॉट का स्क्रीनशॉट अलग किए गए डोम तत्व के बारे में विवरण के साथ यदि अलग किए गए तत्व अभी भी पहचानने में सामान्य हैं, तो अपने परीक्षण को चलाने से पहले ब्राउज़र कंसोल का उपयोग करके उन्हें अद्वितीय आईडी असाइन करें, जैसे:

var divs = document.querySelectorAll("div");
for (var i = 0 ; i < divs.length ; i++)
{
    divs[i].id = divs[i].id || "AutoId_" + i;
}
divs = null; // Free memory

अब, जब आप किसी अलग तत्व की पहचान करते हैं, तो आपको id = "AutoId_49" कहते हैं, अपने पृष्ठ को फिर से लोड करें, ऊपर दिए गए स्निपेट को फिर से निष्पादित करें, और DOM निरीक्षक या document.querySelector (।।) का उपयोग करके id = "AutoId_49" के साथ तत्व ढूंढें। । स्वाभाविक रूप से यह केवल तभी काम करता है जब आपका पेज कंटेंट प्रेडिक्टेबल हो।

मैमोरी लीक की पहचान करने के लिए मैंने अपने परीक्षण कैसे चलाए

1) लोड पृष्ठ (कंसोल आउटपुट के साथ दबा दिया गया है!)

2) उस पेज पर सामान रखें जिससे मेमोरी लीक हो सकती है

3) ढेर स्नैपशॉट लेने और "अलग" के लिए खोज करने के लिए डेवलपर टूल का उपयोग करें

4) उन्हें अपनी आईडी या बाहरी HTML संपत्तियों से पहचानने के लिए तत्व होवर करें


इसके अलावा, यह हमेशा एक अच्छा विचार है कि इसे छोटा करना मुश्किल है क्योंकि यह ब्राउज़र में डीबग करना अधिक कठिन है।
जिमी थॉमसन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.