मेमोरी > प्रोफ़ाइलें > हीप स्नैपशॉट का इस्तेमाल करके, हीप स्नैपशॉट रिकॉर्ड करने और मेमोरी लीक का पता लगाने का तरीका जानें.
हीप प्रोफ़ाइलर से, आपको अपने पेज के JavaScript ऑब्जेक्ट और उनसे जुड़े डीओएम नोड के बीच मेमोरी डिस्ट्रिब्यूशन का पता चलता है. इसका इस्तेमाल, JS ढेर के स्नैपशॉट लेने, मेमोरी ग्राफ़ का विश्लेषण करने, स्नैपशॉट की तुलना करने, और मेमोरी लीक ढूंढने के लिए किया जा सकता है. ज़्यादा जानकारी के लिए, ऑब्जेक्ट के लिए बनाए गए ट्री देखें.
स्नैपशॉट लें
हीप का स्नैपशॉट लेने के लिए:
- जिस पेज की प्रोफ़ाइल बनानी है उस पर DevTools खोलें और मेमोरी पैनल पर जाएं.
- हीप स्नैपशॉट प्रोफ़ाइलिंग टाइप चुनें. इसके बाद, कोई JavaScript वीएम इंस्टेंस चुनें और स्नैपशॉट लें पर क्लिक करें.
जब मेमोरी पैनल लोड होता है और स्नैपशॉट को पार्स करता है, तो यह हीप स्नैपशॉट सेक्शन में स्नैपशॉट के टाइटल के नीचे, ऐसे JavaScript ऑब्जेक्ट का कुल साइज़ दिखाता है जिन तक पहुंचा जा सकता है.
स्नैपशॉट में, मेमोरी ग्राफ़ से सिर्फ़ वे ऑब्जेक्ट दिखते हैं जो ग्लोबल ऑब्जेक्ट से ऐक्सेस किए जा सकते हैं. स्नैपशॉट लेने की प्रोसेस हमेशा गै़रबेज कलेक्शन से शुरू होती है.
स्नैपशॉट मिटाना
सभी स्नैपशॉट हटाने के लिए,
सभी प्रोफ़ाइलें मिटाएं पर क्लिक करें:स्नैपशॉट देखना
अलग-अलग कामों के लिए, अलग-अलग नज़रियों से स्नैपशॉट की जांच करने के लिए, सबसे ऊपर मौजूद ड्रॉप-डाउन मेन्यू से कोई एक व्यू चुनें:
देखें | सामग्री | मकसद |
---|---|---|
खास जानकारी | कन्स्ट्रक्टर के नाम के हिसाब से ग्रुप किए गए ऑब्जेक्ट. | इसका इस्तेमाल करके, ऑब्जेक्ट और उनके मेमोरी इस्तेमाल के बारे में टाइप के हिसाब से पता लगाया जा सकता है. DOM लीक को ट्रैक करने में मददगार. |
Comparison | दो स्नैपशॉट के बीच अंतर. | इसका इस्तेमाल, किसी कार्रवाई से पहले और बाद के दो (या उससे ज़्यादा) स्नैपशॉट की तुलना करने के लिए करें. खाली की गई मेमोरी और रेफ़रंस की संख्या में हुए बदलाव की जांच करके, मेमोरी लीक की मौजूदगी और उसकी वजह की पुष्टि करें. |
कंटेनमेंट | ढेर सारा कॉन्टेंट | इससे ऑब्जेक्ट के स्ट्रक्चर को बेहतर तरीके से देखा जा सकता है. साथ ही, ग्लोबल नेमस्पेस (विंडो) में रेफ़र किए गए ऑब्जेक्ट का विश्लेषण करके, यह पता लगाया जा सकता है कि वे ऑब्जेक्ट क्यों मौजूद हैं. क्लोज़र का विश्लेषण करने और अपने ऑब्जेक्ट को कम लेवल पर देखने के लिए, इसका इस्तेमाल करें. |
आंकड़े | मेमोरी के बंटवारे का पाई चार्ट | कोड, स्ट्रिंग, JS ऐरे, टाइप किए गए ऐरे, और सिस्टम ऑब्जेक्ट के लिए, मेमोरी के हिस्सों के रिलेटिव साइज़ देखें. |
सारांश दृश्य
शुरुआत में, खास जानकारी व्यू में हीप स्नैपशॉट खुलता है. इसमें एक कॉलम में कंस्ट्रक्टर की सूची होती है. कन्स्ट्रक्टर को बड़ा करके, उनसे बनाए गए ऑब्जेक्ट देखे जा सकते हैं.
काम के कॉन्स्ट्रक्टर को फ़िल्टर करने के लिए, खास जानकारी व्यू में सबसे ऊपर मौजूद क्लास फ़िल्टर में वह नाम टाइप करें जिसकी आपको जांच करनी है.
कन्स्ट्रक्टर के नाम के बगल में मौजूद संख्याएं, कन्स्ट्रक्टर की मदद से बनाए गए ऑब्जेक्ट की कुल संख्या दिखाती हैं. खास जानकारी वाले व्यू में ये कॉलम भी दिखते हैं:
- दूरी, नोड के सबसे छोटे और आसान पाथ का इस्तेमाल करके रूट से दूरी दिखाता है.
- शैलो साइज़, किसी खास कन्स्ट्रक्टर से बनाए गए सभी ऑब्जेक्ट के शैलो साइज़ का कुल योग दिखाता है. शैलो साइज़, किसी ऑब्जेक्ट के लिए इस्तेमाल की गई मेमोरी का साइज़ होता है. आम तौर पर, ऐरे और स्ट्रिंग का साइज़ बड़ा होता है. ऑब्जेक्ट के साइज़ भी देखें.
- रिटेंन किया गया साइज़, ऑब्जेक्ट के एक ही सेट में से रिटेंन किए गए सबसे बड़े साइज़ को दिखाता है. सेव किया गया साइज़, मेमोरी का वह साइज़ होता है जिसे किसी ऑब्जेक्ट को मिटाकर और उसके डिपेंडेंट को ऐक्सेस न कर पाकर खाली किया जा सकता है. ऑब्जेक्ट के साइज़ भी देखें.
किसी कन्स्ट्रक्टर को बड़ा करने पर, खास जानकारी व्यू में उसके सभी इंस्टेंस दिखते हैं. हर इंस्टेंस के लिए, उससे जुड़े कॉलम में उसके शैलो और रिटेंन किए गए साइज़ का ब्रेकडाउन दिखता है. @
वर्ण के बाद मौजूद नंबर, ऑब्जेक्ट का यूनीक आईडी होता है. इससे, हर ऑब्जेक्ट के हिसाब से हीप स्नैपशॉट की तुलना की जा सकती है.
कंस्ट्रक्टर फ़िल्टर
खास जानकारी व्यू की मदद से, मेमोरी के गलत इस्तेमाल के सामान्य मामलों के आधार पर कन्स्ट्रक्टर को फ़िल्टर किया जा सकता है.
इन फ़िल्टर का इस्तेमाल करने के लिए, ऐक्शन बार में सबसे दाईं ओर मौजूद ड्रॉप-डाउन मेन्यू से इनमें से कोई एक विकल्प चुनें:
- सभी ऑब्जेक्ट: मौजूदा स्नैपशॉट में कैप्चर किए गए सभी ऑब्जेक्ट. डिफ़ॉल्ट रूप से सेट होता है.
- पहले स्नैपशॉट से पहले एलोकेट किए गए ऑब्जेक्ट: ऐसे ऑब्जेक्ट जो पहले स्नैपशॉट लेने से पहले बनाए गए थे और मेमोरी में बने रहे.
- पहले और दूसरे स्नैपशॉट के बीच ऑब्जेक्ट का बंटवारा: सबसे नए स्नैपशॉट और पिछले स्नैपशॉट के बीच ऑब्जेक्ट में अंतर देखें. हर नए स्नैपशॉट से, ड्रॉप-डाउन सूची में इस फ़िल्टर की संख्या बढ़ जाती है.
- डुप्लीकेट स्ट्रिंग: स्ट्रिंग वैल्यू, जिन्हें मेमोरी में कई बार सेव किया गया है.
- डिटैच किए गए नोड की मदद से सेव किए गए ऑब्जेक्ट: ऐसे ऑब्जेक्ट जिन्हें डिटैच किए गए डीओएम नोड के रेफ़रंस की वजह से सेव रखा जाता है.
- DevTools कंसोल की मदद से सेव किए गए ऑब्जेक्ट: ऐसे ऑब्जेक्ट जिन्हें मेमोरी में इसलिए सेव किया जाता है, क्योंकि DevTools कंसोल की मदद से उनका आकलन किया गया है या उनसे इंटरैक्ट किया गया है.
खास जानकारी में खास एंट्री
कंस्ट्रक्टर के हिसाब से ग्रुप करने के अलावा, खास जानकारी व्यू में ऑब्जेक्ट को इनके हिसाब से भी ग्रुप किया जाता है:
- पहले से मौजूद फ़ंक्शन, जैसे कि
Array
याObject
. - एचटीएमएल एलिमेंट, उनके टैग के हिसाब से ग्रुप किए गए हैं. जैसे,
<div>
,<a>
,<img>
वगैरह. - आपके कोड में तय किए गए फ़ंक्शन.
- खास कैटगरी, जो कन्स्ट्रक्टर पर आधारित नहीं होती हैं.
(array)
इस कैटगरी में, इंटरनल कलेक्शन जैसे कई ऑब्जेक्ट शामिल होते हैं. ये ऑब्जेक्ट, सीधे तौर पर JavaScript में दिखने वाले ऑब्जेक्ट से मेल नहीं खाते.
उदाहरण के लिए, JavaScript Array
ऑब्जेक्ट का कॉन्टेंट, (object elements)[]
नाम के सेकंडरी इंटरनल ऑब्जेक्ट में सेव किया जाता है, ताकि साइज़ को आसानी से बदला जा सके. इसी तरह, JavaScript ऑब्जेक्ट में नाम वाली प्रॉपर्टी को अक्सर (object properties)[]
नाम वाले सेकंडरी इंटरनल ऑब्जेक्ट में सेव किया जाता है. ये ऑब्जेक्ट भी (array)
कैटगरी में शामिल होते हैं.
(compiled code)
इस कैटगरी में वह इंटरनल डेटा शामिल होता है जिसकी V8 को ज़रूरत होती है, ताकि वह JavaScript या WebAssembly के तय किए गए फ़ंक्शन चला सके. हर फ़ंक्शन को कई तरीकों से दिखाया जा सकता है. जैसे, छोटे और धीमे से लेकर बड़े और तेज़ तक.
V8, इस कैटगरी में मेमोरी के इस्तेमाल को अपने-आप मैनेज करता है. अगर कोई फ़ंक्शन कई बार चलता है, तो V8 उस फ़ंक्शन के लिए ज़्यादा मेमोरी का इस्तेमाल करता है, ताकि वह तेज़ी से चल सके. अगर कोई फ़ंक्शन कुछ समय से नहीं चला है, तो V8 उस फ़ंक्शन का इंटरनल डेटा मिटा सकता है.
(concatenated string)
जब V8 दो स्ट्रिंग को जोड़ता है, जैसे कि JavaScript +
ऑपरेटर की मदद से, तो वह नतीजे को "जोडकर बनाई गई स्ट्रिंग" के तौर पर दिखा सकता है. इसे रॉप डेटा स्ट्रक्चर भी कहा जाता है.
V8, दो सोर्स स्ट्रिंग के सभी वर्णों को नई स्ट्रिंग में कॉपी करने के बजाय, first
और second
नाम के इंटरनल फ़ील्ड के साथ एक छोटा ऑब्जेक्ट असाइन करता है. यह ऑब्जेक्ट, दो सोर्स स्ट्रिंग पर ले जाता है. इससे V8 को समय और मेमोरी बचाने में मदद मिलती है. JavaScript कोड के हिसाब से, ये सिर्फ़ सामान्य स्ट्रिंग हैं और ये किसी भी दूसरी स्ट्रिंग की तरह काम करती हैं.
InternalNode
यह कैटगरी, V8 के बाहर असाइन किए गए ऑब्जेक्ट दिखाती है. जैसे, Blink के ज़रिए तय किए गए C++ ऑब्जेक्ट.
C++ क्लास के नाम देखने के लिए, Chrome for Testing का इस्तेमाल करें और ये काम करें:
- DevTools खोलें और Settings > Experiments > Show option to expose internals in heap snapshots को चालू करें.
- मेमोरी पैनल खोलें. इसके बाद, हीप स्नैपशॉट चुनें और इंटरनल डेटा दिखाएं (इसमें, इसे इस्तेमाल करने से जुड़ी अन्य ज़रूरी जानकारी शामिल है) को चालू करें.
- उस समस्या को दोहराएं जिसकी वजह से
InternalNode
में बहुत ज़्यादा मेमोरी सेव हो रही है. - हीप का स्नैपशॉट लें. इस स्नैपशॉट में, ऑब्जेक्ट के पास
InternalNode
के बजाय C++ क्लास के नाम हैं.
(object shape)
V8 में फ़ास्ट प्रॉपर्टी में बताए गए तरीके के मुताबिक, V8 छिपी हुई क्लास (या आकार) को ट्रैक करता है, ताकि एक ही क्रम में एक ही प्रॉपर्टी वाले कई ऑब्जेक्ट को बेहतर तरीके से दिखाया जा सके. इस कैटगरी में, system / Map
(JavaScript Map
से जुड़ी नहीं) नाम की छिपी हुई क्लास और उससे जुड़ा डेटा शामिल होता है.
(sliced string)
जब V8 को कोई सबस्ट्रिंग लेनी हो, जैसे कि JavaScript कोड String.prototype.substring()
को कॉल करने पर, V8 ओरिजनल स्ट्रिंग के सभी काम के वर्णों को कॉपी करने के बजाय, स्लाइस की गई स्ट्रिंग ऑब्जेक्ट को असाइन कर सकता है. इस नए ऑब्जेक्ट में ओरिजनल स्ट्रिंग का पॉइंटर होता है. साथ ही, यह भी बताया जाता है कि ओरिजनल स्ट्रिंग के किन वर्णों का इस्तेमाल करना है.
JavaScript कोड के हिसाब से, ये सिर्फ़ सामान्य स्ट्रिंग हैं और ये किसी भी दूसरी स्ट्रिंग की तरह काम करती हैं. अगर स्लाइस की गई स्ट्रिंग बहुत ज़्यादा मेमोरी का इस्तेमाल कर रही है, तो हो सकता है कि प्रोग्राम ने समस्या 2869 को ट्रिगर किया हो. ऐसे में, स्लाइस की गई स्ट्रिंग को "फ़्लैट" करने के लिए, जान-बूझकर कुछ कदम उठाने से फ़ायदा हो सकता है.
system / Context
system / Context
टाइप के इंटरनल ऑब्जेक्ट में, क्लोज़र के लोकल वैरिएबल होते हैं. क्लोज़र, JavaScript का एक स्कोप होता है जिसे नेस्ट किया गया फ़ंक्शन ऐक्सेस कर सकता है.
हर फ़ंक्शन इंस्टेंस में, उस Context
का इंटरनल पॉइंटर होता है जिसमें वह फ़ंक्शन काम करता है. इससे, फ़ंक्शन उन वैरिएबल को ऐक्सेस कर पाता है. भले ही, Context
ऑब्जेक्ट सीधे तौर पर JavaScript से न दिखें, लेकिन आपके पास उन पर सीधे तौर पर कंट्रोल होता है.
(system)
इस कैटगरी में ऐसे कई इंटरनल ऑब्जेक्ट शामिल हैं जिन्हें अब तक किसी काम के तरीके से कैटगरी में नहीं रखा गया है.
तुलना वाला व्यू
तुलना व्यू की मदद से, कई स्नैपशॉट की आपस में तुलना करके, लीक हुए ऑब्जेक्ट ढूंढे जा सकते हैं. उदाहरण के लिए, किसी दस्तावेज़ को खोलने और बंद करने जैसी कोई कार्रवाई करने और उसे पहले जैसा करने पर, अतिरिक्त ऑब्जेक्ट नहीं होने चाहिए.
यह पुष्टि करने के लिए कि कोई खास कार्रवाई, डेटा लीक नहीं करती:
- कोई कार्रवाई करने से पहले, हीप का स्नैपशॉट लें.
- कोई कार्रवाई करें. इसका मतलब है कि किसी ऐसे पेज से इंटरैक्ट करें जिससे आपको लगता है कि डेटा लीक हो सकता है.
- रिवर्स ऑपरेशन करें. इसका मतलब है कि उस इंटरैक्शन को उलटा करें और उसे कुछ बार दोहराएं.
- दूसरा हीप स्नैपशॉट लें और उसका व्यू बदलकर तुलना करें. इसके बाद, इसकी तुलना पहले स्नैपशॉट से करें.
तुलना व्यू, दो स्नैपशॉट के बीच का अंतर दिखाता है. कुल एंट्री को बड़ा करने पर, जोड़े गए और मिटाए गए ऑब्जेक्ट इंस्टेंस दिखाए जाते हैं:
कंटेनमेंट व्यू
कंटेनमेंट व्यू, आपके ऐप्लिकेशन के ऑब्जेक्ट के स्ट्रक्चर का "विहंगम दृश्य" होता है. इसकी मदद से, फ़ंक्शन क्लोज़र के अंदर झांका जा सकता है. साथ ही, उन VM इंटरनल ऑब्जेक्ट को देखा जा सकता है जो आपके JavaScript ऑब्जेक्ट को एक साथ बनाते हैं. इससे यह भी समझा जा सकता है कि आपका ऐप्लिकेशन, सबसे निचले लेवल पर कितनी मेमोरी का इस्तेमाल करता है.
इस व्यू में कई एंट्री पॉइंट होते हैं:
- DOMWindow ऑब्जेक्ट. JavaScript कोड के लिए ग्लोबल ऑब्जेक्ट.
- GC रूट. जीसी रूट, जिनका इस्तेमाल VM के कचरा कलेक्टर (जीसी) करता है. जीसी रूट में, पहले से मौजूद ऑब्जेक्ट मैप, सिंबल टेबल, VM थ्रेड स्टैक, कंपाइलेशन कैश मेमोरी, हैंडल स्कोप, और ग्लोबल हैंडल शामिल हो सकते हैं.
- नेटिव ऑब्जेक्ट. ऑटोमेशन की सुविधा देने के लिए, JavaScript वर्चुअल मशीन में "पुश किए गए" ब्राउज़र ऑब्जेक्ट. उदाहरण के लिए, डीओएम नोड और सीएसएस नियम.
रिटेनर सेक्शन
यादें पैनल में सबसे नीचे मौजूद रिटेंनर सेक्शन में, वे ऑब्जेक्ट दिखते हैं जो व्यू में चुने गए ऑब्जेक्ट पर ले जाते हैं. आंकड़े के अलावा किसी भी व्यू में कोई दूसरा ऑब्जेक्ट चुनने पर, मेमोरी पैनल, रिटेंनर सेक्शन को अपडेट कर देता है.
इस उदाहरण में, चुनी गई स्ट्रिंग को Item
इंस्टेंस की x
प्रॉपर्टी में सेव किया जाता है.
रिटेनर को अनदेखा करना
किसी दूसरे ऑब्जेक्ट में चुने गए ऑब्जेक्ट को बनाए रखने के लिए, रिटेनर छिपाए जा सकते हैं. इस विकल्प का इस्तेमाल करने पर, आपको कोड से इस रिटेनर को हटाने के बाद, हेप का स्नैपशॉट फिर से लेने की ज़रूरत नहीं होती.
किसी रिटेनर को छिपाने के लिए, उस पर राइट क्लिक करें और इस रिटेनर को अनदेखा करें को चुनें. अनदेखा किए गए रिटेनर को दूरी कॉलम में ignored
के तौर पर मार्क किया जाता है. सभी रिटेनर को अनदेखा करने की सुविधा बंद करने के लिए, सबसे ऊपर मौजूद ऐक्शन बार में, अनदेखा किए गए रिटेनर को वापस लाएं पर क्लिक करें.
कोई खास ऑब्जेक्ट ढूंढना
इकट्ठा किए गए ढेर में किसी ऑब्जेक्ट को ढूंढने के लिए, Ctrl + F का इस्तेमाल करके खोजें और ऑब्जेक्ट आईडी डालें.
क्लोज़र में अंतर करने के लिए फ़ंक्शन को नाम देना
फ़ंक्शन के नाम रखने से, स्नैपशॉट में क्लोज़र के बीच का फ़र्क़ पता चलता है.
उदाहरण के लिए, नीचे दिया गया कोड, नाम वाले फ़ंक्शन का इस्तेमाल नहीं करता:
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function() { // this is NOT a named function
return largeStr;
};
return lC;
}
जबकि इस उदाहरण में:
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function lC() { // this IS a named function
return largeStr;
};
return lC;
}
डीओएम लीक का पता लगाना
हीप प्रोफ़ाइलर, ब्राउज़र-नेटिव ऑब्जेक्ट (डीओएम नोड और सीएसएस नियम) और JavaScript ऑब्जेक्ट के बीच, दोनों तरफ़ की डिपेंडेंसी दिखा सकता है. इससे, अलग हो चुके डीओएम सबट्री की वजह से होने वाली लीक का पता चलता है.
डीओएम से लीक होने वाली जानकारी आपकी सोच से ज़्यादा हो सकती है. नीचे दिया गया उदाहरण देखें. #tree
कचरा कब इकट्ठा किया जाता है?
var select = document.querySelector;
var treeRef = select("#tree");
var leafRef = select("#leaf");
var body = select("body");
body.removeChild(treeRef);
//#tree can't be GC yet due to treeRef
treeRef = null;
//#tree can't be GC yet due to indirect
//reference from leafRef
leafRef = null;
//#NOW #tree can be garbage collected
#leaf
अपने पैरंट (parentNode
) का रेफ़रंस रखता है और #tree
तक बार-बार रेफ़रंस रखता है. इसलिए, leafRef
के शून्य होने पर ही #tree
के नीचे मौजूद पूरा ट्री, जीसी के लिए उम्मीदवार होता है.