प्रांप्ट और लागू संदर्भ
एक React सिंगल-पेज ऑर्डर डैशबोर्ड में एक लाइव चार्ट, एक ऑर्डर-इवेंट सब्सक्रिप्शन और रिस्पॉन्सिव लेआउट व्यवहार शामिल है। एक यूज़र ऑर्डर डिटेल रूट खोलता है, लिस्ट पर वापस आता है, और इस प्रक्रिया को 20 बार दोहराता है। इसके बाद पेज धीमा हो जाता है। Chrome Performance रिकॉर्डिंग से पता चलता है कि हर राउंड के अंत में एक ही ऑपरेशन और एक गारबेज-कलेक्शन अवसर के बाद भी, JS हीप का लो-वॉटर मार्क बढ़ता रहता है। DOM नोड और लिसनर की संख्या भी अपनी शुरुआती रेंज में वापस नहीं आ पाती है।
संबंधित कंपोनेंट को नीचे सरलीकृत रूप में दिखाया गया है:
useEffect(() => {
const chart = createOrderChart(containerRef.current)
window.addEventListener("resize", () => chart.resize())
orderBus.on("order", (order) => chart.append(order))
const timerId = window.setInterval(() => chart.refresh(), 5_000)
return () => {
window.clearInterval(timerId)
}
}, [])इंटरव्यू में एक समीक्षा योग्य साक्ष्य श्रृंखला (reviewable evidence chain) की मांग की गई है। उम्मीदवार को सामान्य एलोकेशन, मेमोरी ब्लोट, लगातार गारबेज कलेक्शन और एक वास्तविक लीक के बीच अंतर स्पष्ट करना होगा; यह पहचानना होगा कि GC रूट से किस वजह से वह ऑब्जेक्ट बना रहता है जिसे नष्ट हो जाना चाहिए था; लिसनर, सब्सक्रिप्शन, टाइमर और थर्ड-पार्टी इंस्टेंस के लाइफ़टाइम को ठीक करना होगा; और उसी एक्शन लूप के साथ फिक्स को साबित करना होगा।
2025 का एक सार्वजनिक फ़्रंटएंड प्रश्न बैंक स्पष्ट रूप से पूछता है कि JavaScript मेमोरी लीक्स की पहचान और समाधान कैसे करें। 2025 की एक अन्य JavaScript इंटरव्यू गाइड एक ऐसे SPA का उपयोग करती है जो बार-बार नेविगेशन के बाद धीमा हो जाता है और पूछती है कि DevTools लीक का पता कैसे लगाएगा। हाल ही का एक सार्वजनिक इंटरव्यू विवरण गारबेज कलेक्शन, क्लोज़र, लिसनर और कंपोनेंट अनमाउंटिंग से जुड़े फ़ॉलो-अप रिकॉर्ड करता है। खोज का उद्देश्य ठोस है: उम्मीदवारों को एक निष्पादन योग्य ब्राउज़र-डायग्नोस्टिक्स वर्कफ़्लो की आवश्यकता है, न कि केवल "टाइमर साफ़ करें" पर समाप्त होने वाली सूची की।
इंटरव्यूअर क्या मूल्यांकन कर रहा है
पहला कौशल एक मान्य निर्णय नियम निर्धारित करना है। केवल ऑब्जेक्ट्स में अस्थायी वृद्धि, एक उच्च पीक, या बढ़ता हुआ प्रोसेस-मेमोरी नंबर अपने आप में लीक साबित नहीं करता है। एक मजबूत उत्तर ब्राउज़र, बिल्ड, डेटा और एक्शन सीक्वेंस को स्थिर रखता है; राउंड्स के बीच कलेक्शन को एक तुलनीय अवसर देता है; और कई लाइव बेसलाइन्स की तुलना करता है। सामान्य एलोकेशन एक सॉटूथ (sawtooth) पैटर्न बना सकता है जो बढ़ता और घटता है। जो ऑब्जेक्ट एक ही ऑपरेशन के बाद भी बचे रहते हैं और जमा होते रहते हैं, वे उनके रेफरेंस पाथ की जांच को उचित ठहराते हैं।
दूसरा कौशल रीचेबिलिटी (reachability) है। आधुनिक JavaScript गारबेज कलेक्टर्स उन ऑब्जेक्ट्स को मार्क करते हैं जो ग्लोबल ऑब्जेक्ट जैसे रूट्स से रीचेबल होते हैं। एप्लिकेशन द्वारा किसी ऑब्जेक्ट की आवश्यकता न होना, कलेक्टर के लिए उस तथ्य को दृश्यमान नहीं बनाता है। यदि कोई window लिसनर, इवेंट बस, टाइमर, कैश या थर्ड-पार्टी लाइब्रेरी अभी भी एक मजबूत रेफरेंस रखती है, तो ऑब्जेक्ट रीचेबल बना रहता है। केवल एक साइकिल से लीक होना ज़रूरी नहीं है: एक पूरा साइक्लिक ग्रुप तब कलेक्ट किया जा सकता है जब रूट से कोई पाथ उस तक नहीं पहुंचता।
तीसरा कौशल केवल Memory पैनल खोलने के बजाय साक्ष्यों को पढ़ना है:
| साक्ष्य | यह किस प्रश्न का उत्तर देता है | यह अकेले क्या साबित नहीं कर सकता |
|---|---|---|
| Task Manager / Performance मेमोरी कर्व | क्या दोहराया गया कार्य मेमोरी बढ़ाता रहता है? | कौन सा ऑब्जेक्ट इसे बनाए रखता है (retains) |
| Heap Snapshot Comparison | कौन से लाइव प्रकार और रेफरेंस काउंट बढ़े? | क्या वृद्धि किसी कैश अनुबंध को संतुष्ट करती है |
| Retainers / रिटेनिंग पाथ | कौन सी रेफरेंस चेन किसी ऑब्जेक्ट को रूट से जोड़ती है? | ओनर कोड को इसे कब रिलीज़ करना चाहिए |
| Allocation instrumentation on timeline | किस एक्शन ने उन ऑब्जेक्ट्स को एलोकेट किया जो बाद में बचे रहे? | कि हर एलोकेशन एक लीक है |
| DOM नोड, डॉक्यूमेंट और लिसनर काउंट्स | क्या वृद्धि DOM- या लिसनर-उन्मुख है? | JS हीप के बाहर पूरी प्रोसेस मेमोरी |
अंतिम कौशल रिसोर्स ओनरशिप है। जिस लाइफ़साइकल बाउंड्री ने रिसोर्स बनाया है, उसे ही इसे रिलीज़ करना चाहिए। AbortController अपने signal के साथ पंजीकृत DOM लिसनर्स को हटा सकता है, लेकिन यह ResizeObserver को डिस्कनेक्ट नहीं करता, WebSocket को बंद नहीं करता, इवेंट बस को अनसब्सक्राइब नहीं करता या थर्ड-पार्टी चार्ट को नष्ट नहीं करता है। उन रिसोर्सेस को अभी भी अपने स्वयं के disconnect, close, unsubscribe, या destroy ऑपरेशन की आवश्यकता होती है।
उत्तर देने से पहले स्पष्ट करने योग्य प्रश्न
- समस्या को वास्तव में क्या रीप्रोड्यूस करता है? एंट्री पॉइंट, रिटर्न एक्शन, प्रति-राउंड तैयारी की स्थिति, डेटा वॉल्यूम और रिपीट काउंट को निर्धारित करें। प्रत्येक राउंड में अलग-अलग ऑर्डर स्नैपशॉट अंतर को व्यावसायिक-डेटा अंतरों के साथ दूषित कर देते हैं।
- कौन सा माप बढ़ता है? OS मेमोरी फ़ुटप्रिंट, JS हीप, DOM नोड्स, डॉक्यूमेंट्स और लिसनर्स अलग-अलग क्षेत्रों को कवर करते हैं। स्नैपशॉट या एलोकेशन विश्लेषण चुनने से पहले समस्या को संकीर्ण करें।
- क्या प्रोडक्ट जानबूझकर कुछ कैश करता है? एक बाउंडेड रूट कैश या चार्ट हिस्ट्री डिज़ाइन के अनुसार लाइव रह सकती है। क्षमता, निष्कासन (eviction) और स्थिर-अवस्था की अपेक्षाएं नियंत्रित उपयोग को लीक से अलग करती हैं।
- क्या कंपोनेंट वास्तव में अनमाउंट होता है? रूटिंग इसे छिपा सकती है, संरक्षित कर सकती है या पुन: उपयोग कर सकती है। कभी न होने वाले अनमाउंट को ठीक करने से पहले माउंट और क्लीनअप लॉग के साथ वास्तविक लाइफ़साइकल की पुष्टि करें।
- कौन से ऑब्जेक्ट थर्ड-पार्टी लाइब्रेरी से आते हैं? चार्ट, एडिटर और मैप्स अक्सर DOM, वर्कर्स, ऑब्ज़र्वर्स और आंतरिक लिसनर्स के मालिक होते हैं। कंटेनर HTML को साफ़ करने से इंस्टेंस नष्ट नहीं होता है।
- क्या प्रोडक्शन के लक्षण को टेस्ट वातावरण में रीप्रोड्यूस किया जा सकता है? हीप स्नैपशॉट में यूज़र डेटा हो सकता है। सैनिटाइज़्ड डेटा के साथ नियंत्रित रीप्रोडक्शन को प्राथमिकता दें और गोपनीयता व एक्सेस नियंत्रण लागू करें।
- सफलता क्या है? डिवाइस और वर्कलोड से स्वतंत्र कोई सार्वभौमिक MB सीमा नहीं है। पोस्ट-ऑपरेशन बेसलाइन स्लोप, लाइव इंस्टेंस काउंट, नोड/लिसनर काउंट और यूज़र को दिखने वाले स्टॉल्स पर सहमति बनाएं।
30-सेकंड उत्तर फ़्रेमवर्क
"मैं एक लिस्ट-डिटेल-लिस्ट एक्शन तय करूंगा, वार्म-अप करूंगा, इसे दोहराऊंगा और समान GC चेकपॉइंट्स पर लाइव बेसलाइन्स की तुलना करूंगा। यदि हीप लो पॉइंट्स, DOM नोड्स या लिसनर्स जमा होते हैं, तो मैं स्नैपशॉट की तुलना करता हूं, बढ़ते प्रकारों का पता लगाता हूं, और Retainers को window, इवेंट बस, या टाइमर तक ट्रैक करता हूं। यदि स्रोत अस्पष्ट है तो मैं Allocation timeline जोड़ता हूं। फिर क्रिएटर लिसनर्स, सब्सक्रिप्शन, टाइमर, ऑब्ज़र्वर्स और चार्ट्स को रिलीज़ करता है। मैं उसी लूप को दोबारा चलाता हूं और मांग करता हूं कि इंस्टेंस काउंट कम हो, बेसलाइन्स स्थिर हों, और व्यवहार सही बना रहे।"
स्टेप-बाय-स्टेप डीप डाइव
स्टेप 1: दोहराए गए प्रयोग के साथ लीक को साबित करें
एक तुलनीय बेसलाइन के साथ शुरुआत करें। असंबंधित एक्सटेंशन को न्यूनतम रखते हुए उसी Chrome वर्शन का उपयोग करें, और विंडो, टेस्ट अकाउंट, ऑर्डर डेटा और रूट को फ़िक्स करें। रीलोड के बाद, एक वार्म-अप पूरा करें ताकि लेज़ी मॉड्यूल, फ़ॉन्ट, कनेक्शन पूल और वन-टाइम कैश इनिशियलाइज़ हो जाएं। Performance पैनल में, Memory सक्षम करें और चलाएं:
- एक बार गारबेज कलेक्शन ट्रिगर करें और शुरुआती बिंदु रिकॉर्ड करें।
- हर बार उसी "chart rendered" स्थिति की प्रतीक्षा करते हुए, पांच लिस्ट-डिटेल-लिस्ट राउंड पूरे करें।
- गारबेज कलेक्शन को फिर से ट्रिगर करें और JS हीप, DOM नोड, डॉक्यूमेंट और लिसनर लो पॉइंट्स को रिकॉर्ड करें।
- तीन समूहों को दोहराएं और मनमाने पीक्स के बजाय ग्रुप लो पॉइंट्स की तुलना करें।
कलेक्शन का समय रनटाइम के अधीन होता है। एक छोटा प्रयोग JIT कार्य, इमेज डिकोडिंग, नेटवर्क प्रतिक्रियाओं और स्वयं DevTools से भी प्रभावित होता है, इसलिए एक-राउंड का अंतर केवल एक परिकल्पना बनाता है। एक उच्च पहला समूह जो बाद में स्थिर हो जाता है, वह वार्म-अप हो सकता है। यदि प्रत्येक समूह एक्शन काउंट के अनुपात में एक नया OrderChart, डिटेच्ड नोड्स का एक सेट और दो लिसनर्स बनाए रखता है, तो साक्ष्य अधिक मजबूत होता है।
तीन घटनाओं को अलग रखें:
- लीक: समान ऑपरेशन के बाद पोस्ट-GC लाइव बेसलाइन्स बढ़ती रहती हैं।
- मेमोरी ब्लोट: स्थिर-अवस्था का उपयोग अत्यधिक है लेकिन अब ऑपरेशन काउंट के साथ असीम रूप से नहीं बढ़ता है।
- एलोकेशन चर्न: बार-बार GC पॉज़ के साथ हीप बढ़ता और घटता है, और लो पॉइंट्स रिकवर हो जाते हैं; बहुत अधिक अस्थायी एलोकेशन ही समस्या है।
Task Manager का Memory फ़ुटप्रिंट DOM स्टोरेज जैसे प्रोसेस उपयोग को शामिल करता है। JavaScript Memory में लाइव वैल्यू रीचेबल JS हीप के करीब होती है। ये माप जांच को प्राथमिकता देते हैं, लेकिन वे हीप स्नैपशॉट के रेफरेंस साक्ष्य की जगह नहीं लेते हैं। फ़ोर्स्ड GC एक डायग्नोस्टिक नियंत्रण है, कोई प्रोडक्ट फिक्स नहीं, और प्रोडक्शन कोड किसी विशेष क्षण में कलेक्शन का वादा नहीं कर सकता है।
स्टेप 2: स्नैपशॉट और रिटेनिंग पाथ की मदद से जिम्मेदार ओनर का पता लगाएं
वृद्धि स्थापित करने के बाद, Memory पैनल में Snapshot A लें। निश्चित नेविगेशन लूप निष्पादित करें, लिस्ट पर वापस आएं, एसिंक्रोनस क्लीनअप की प्रतीक्षा करें, और Snapshot B लें। स्नैपशॉट कैप्चर गारबेज कलेक्शन से शुरू होता है, इसलिए रीचेबल रहने वाले ऑब्जेक्ट्स के लिए Comparison उपयोगी है। इंस्टेंस डेल्टा और रिटेन किए गए आकार के आधार पर सॉर्ट करें, और बिज़नेस कंस्ट्रक्टर्स, क्लोज़र्स, एरेज़ और डिटेच्ड DOM ट्रीज़ की तलाश करें जो लूप काउंट के साथ बढ़ते हैं।
shallow size ऑब्जेक्ट का स्वयं का वर्णन करता है। retained size उस मेमोरी का अनुमान लगाता है जो उस ऑब्जेक्ट के अनरीचेबल होने पर रिलीज़ हो सकती है। एक छोटा लिसनर कॉलबैक अपने क्लोज़र के माध्यम से एक चार्ट, एक डेटा एरे और एक पूरे DOM सब-ट्री को बनाए रख सकता है, इसलिए रिटेन किया गया आकार कॉलबैक आकार की तुलना में एक बेहतर प्राथमिकता संकेत है। यह एक जांच अनुमान है, अनन्य भौतिक मेमोरी का प्रत्यक्ष दावा नहीं।
एक OrderChart या डिटेच्ड नोड चुनें जिसे समाप्त हो जाना चाहिए था और Retainers का निरीक्षण करें। एक पाथ ऐसा दिख सकता है:
Window
└─ resize event listener
└─ callback closure
└─ chart
└─ container
└─ detached HTMLDivElementयह पाथ बताता है कि GC ग्राफ़ को कलेक्ट क्यों नहीं कर सकता: ग्लोबल window अभी भी अनाम रीसाइज़ कॉलबैक का मालिक है, जिसका क्लोज़र चार्ट का मालिक है। डॉक्यूमेंट से DOM को हटाने से डॉक्यूमेंट के साथ इसका संबंध बदल जाता है; यह रूट से JavaScript पाथ को नहीं तोड़ता है। सुधार लिसनर और चार्ट ओनरशिप पर होना चाहिए, न कि डिटेच्ड नोड को एक सट्टा null असाइनमेंट पर।
यदि स्नैपशॉट केवल सामान्य Object और Array प्रविष्टियां दिखाते हैं, तो Allocation instrumentation on timeline का उपयोग करें। रिकॉर्डिंग शुरू करें, ठीक एक लीकिंग एक्शन निष्पादित करें, रोकें, और उन एलोकेशन्स पर ध्यान केंद्रित करें जो कलेक्शन के दौरान लाइव रहते हैं। उनके कंस्ट्रक्टर और एलोकेशन स्टैक को वापस कोड तक ट्रैक करें। Allocation sampling में कम ओवरहेड होता है और यह एलोकेशन हॉट फ़ंक्शंस को खोजने के लिए उपयोगी है, लेकिन जब अलग-अलग इंस्टेंसेस और रिटेनर्स मायने रखते हैं तो इसका सैंपल्ड परिणाम स्नैपशॉट की जगह नहीं लेता है।
सामान्य रिटेंशन स्रोतों में शामिल हैं:
- ग्लोबल ऑब्जेक्ट्स या मॉड्यूल एरेज़ जो पेज इंस्टेंस को अनिश्चित काल तक जोड़ते हैं;
- DOM/EventTarget लिसनर्स जिन्हें कभी नहीं हटाया गया, या एक अलग फ़ंक्शन पहचान या कैप्चर विकल्प के साथ हटाया गया था;
setIntervalकॉलबैक और रिकर्सिव टाइमआउट जो कंपोनेंट स्टेट को बनाए रखते हैं;- बिना अनसब्सक्राइब किए इवेंट बसें, स्टोर्स, WebSockets, या ऑब्ज़र्वेबल्स;
- बिना डिस्ट्रक्शन के
ResizeObserver,IntersectionObserver, वर्कर्स और थर्ड-पार्टी इंस्टेंसेस; - बिना क्षमता सीमा वाले Maps, हिस्ट्री कलेक्शन्स, या रिक्वेस्ट कैश;
- प्रॉमिस जो कभी सेटल नहीं होते या कतारबद्ध कार्य जो असीमित समय के लिए क्लोज़र्स को बनाए रखते हैं।
स्टेप 3: रिसोर्स ओनरशिप को ठीक करें और वही एक्सेप्टेंस टेस्ट दोबारा चलाएं
सुधारा गया Effect प्रत्येक रिसोर्स के लिए एक रिलीज़ हैंडल बनाए रखता है। AbortController DOM लिसनर्स का मालिक हो सकता है, जबकि शेष रिसोर्स स्पष्ट रूप से रिलीज़ किए जाते हैं:
useEffect(() => {
const container = containerRef.current
if (!container) return
const controller = new AbortController()
const chart = createOrderChart(container)
const observer = new ResizeObserver(() => chart.resize())
const unsubscribe = orderBus.on("order", (order) => chart.append(order))
const timerId = window.setInterval(() => chart.refresh(), 5_000)
observer.observe(container)
window.addEventListener("visibilitychange", () => chart.syncVisibility(), {
signal: controller.signal,
})
return () => {
controller.abort()
observer.disconnect()
unsubscribe()
window.clearInterval(timerId)
chart.destroy()
}
}, [])वास्तविक API अनुबंध की पुष्टि करें। कुछ on मेथड एक अनसब्सक्राइब फ़ंक्शन लौटाते हैं; दूसरों के लिए उसी हैंडलर को off को प्रदान करने की आवश्यकता होती है। यदि बदलती निर्भरताएं किसी रिसोर्स को दोबारा बना सकती हैं, तो क्लीनअप को केवल उस Effect रन द्वारा बनाए गए इंस्टेंस को रिलीज़ करना चाहिए और उसके उत्तराधिकारी को बंद नहीं करना चाहिए। एसिंक्रोनस अनुरोधों को कैंसिलेशन या जेनरेशन जांच की भी आवश्यकता होती है। अनमाउंट के बाद स्टेट राइट को रोकना एक प्रभाव को संबोधित करता है; यदि कोई बाहरी सब्सक्रिप्शन अभी भी क्लोज़र का मालिक है तो लीक बना रहता है।
WeakMap ऑब्जेक्ट-कीड मेटाडेटा के लिए उपयुक्त है, लेकिन यह स्पष्ट लाइफ़साइकल क्लीनअप की जगह नहीं लेता है। WeakRef जानबूझकर कलेक्शन समय और दृश्यमान व्यवहार के बारे में बहुत कम गारंटी प्रदान करता है, और यह आमतौर पर लिसनर्स, कनेक्शन्स या चार्ट इंस्टेंसेस के लिए एक खराब समाधान है। सीधा समाधान अनावश्यक मजबूत रेफरेंस को तोड़ना और जानबूझकर बनाए गए कैश को क्षमता, TTL या निष्कासन शर्त प्रदान करना है।
स्वीकृति परीक्षण प्री-फिक्स प्रयोग को सटीक रूप से दोबारा चलाता है: वही बिल्ड, डेटा, नेविगेशन काउंट, तत्परता बिंदु और GC नियंत्रण। पास होने की शर्तों में शामिल हैं:
- लाइव डिटेल कंपोनेंट्स, चार्ट इंस्टेंसेस और डिटेच्ड सब-ट्रीज़ छोड़ने के बाद सहमत संख्या पर लौट आते हैं;
- कई समूहों में, पोस्ट-GC बेसलाइन्स नेविगेशन काउंट को लगभग रैखिक रूप से ट्रैक करने के बजाय एक स्थिर सीमा के भीतर उतार-चढ़ाव करती हैं;
- लिसनर, डॉक्यूमेंट और DOM नोड काउंट अपनी अपेक्षित सीमा पर लौट आते हैं;
- प्रत्येक माउंट एक मैसेज सब्सक्रिप्शन और एक चार्ट इंस्टेंस का मालिक होता है, और प्रत्येक अनमाउंट प्रत्येक को एक बार रिलीज़ करता है;
- चार्ट अभी भी अपडेट होते हैं, कंटेनर का आकार बदलना और दृश्यता व्यवहार काम करता है, और दोबारा विज़िट करने पर रिसोर्स फिर से बनते हैं;
- लंबे समय तक चलने वाले स्टॉल्स, GC पॉज़ और क्रैश सिग्नल प्रोडक्ट बजट को पूरा करते हैं।
समीक्षा के लिए पहले और बाद के स्नैपशॉट, रीप्रोडक्शन स्टेप्स, बिल्ड पहचान और मुख्य रिटेनिंग पाथ को बनाए रखें। एक स्नैपशॉट में स्ट्रिंग्स और व्यावसायिक ऑब्जेक्ट्स हो सकते हैं, इसलिए इसे संवेदनशील डिबगिंग सामग्री के रूप में स्टोर और शेयर करें।
उच्च-गुणवत्ता वाला नमूना उत्तर
"कर्व एक मजबूत साक्ष्य है, लेकिन केवल एक वृद्धि मेरे निष्कर्ष के लिए पर्याप्त नहीं है। मैं ऑर्डर डेटा को ठीक करूंगा, लिस्ट-डिटेल-लिस्ट को एक ऑपरेशन के रूप में परिभाषित करूंगा, वार्म-अप करूंगा, फिर पांच-पांच के तीन समूह चलाऊंगा। प्रत्येक समूह से पहले और बाद में समान पेज स्थितियों पर, मैं GC का अनुरोध करूंगा और हीप लो पॉइंट्स, DOM नोड्स, डॉक्यूमेंट्स और लिसनर्स को रिकॉर्ड करूंगा। यदि केवल पहला समूह बढ़ता है और बाद के समूह स्थिर हो जाते हैं, तो मैं इनिशियलाइज़ेशन या एक बाउंडेड कैश का निरीक्षण करता हूं। यदि प्रत्येक समूह समान संख्या में चार्ट और लिसनर्स जोड़ता है, तो मैं स्नैपशॉट की ओर बढ़ता हूं।"
"मैं वार्म-अप के बाद Snapshot A लेता हूं, लूप चलाता हूं, लिस्ट पर लौटता हूं, क्लीनअप की प्रतीक्षा करता हूं, और Snapshot B लेता हूं। Comparison में मैं इंस्टेंस और रिटेन-साइज़ डेल्टा के साथ शुरुआत करता हूं। मान लीजिए कि मुझे 15 OrderChart इंस्टेंस मिलते हैं जिन्हें समाप्त हो जाना चाहिए था, जिनके Retainers में Window → resize listener → closure → chart → detached container है। यह पाथ विफलता की व्याख्या करता है: window अभी भी अनाम लिसनर का मालिक है, जिसका क्लोज़र चार्ट और DOM को बनाए रखता है। यदि कंस्ट्रक्टर के नाम सामान्य हैं, तो मैं एक Allocation timeline रिकॉर्ड करता हूं और उस नेविगेशन से बचे हुए ऑब्जेक्ट्स को कोड पर वापस मैप करने के लिए इसके एलोकेशन स्टैक का उपयोग करता हूं।"
"सुधार के लिए, Effect प्रत्येक रिलीज़ हैंडल को बनाए रखता है: DOM लिसनर्स AbortController का उपयोग करते हैं, इवेंट बस अनसब्सक्राइब करती है, टाइमर साफ़ होता है, ऑब्ज़र्वर डिस्कनेक्ट होता है, और चार्ट नष्ट हो जाता है। यदि किसी लाइब्रेरी को off(handler) की आवश्यकता होती है, तो मैं उसी हैंडलर पहचान को सुरक्षित रखता हूं। कैश को क्षमता या निष्कासन मिलता है। मैं वही प्रयोग दोबारा चलाता हूं और केवल तभी पास करता हूं जब चार्ट और डिटेच्ड सब-ट्री सहमत संख्या पर वापस आ जाते हैं, पोस्ट-GC बेसलाइन नेविगेशन काउंट को ट्रैक करना बंद कर देती है, और फिर से विज़िट करने पर फिर से सब्सक्राइब और रेंडर होता है।"
सामान्य गलतियां
- एक पीक से लीक की घोषणा करना → सामान्य एलोकेशन, JIT कार्य और कैश भी पीक बढ़ाते हैं → कई पोस्ट-GC बेसलाइन्स की तुलना करें और ऑपरेशन काउंट के साथ इंस्टेंस काउंट को सहसंबंधित करें।
- डिटेच्ड DOM देखने के बाद कंटेनर को साफ़ करना → एक ग्लोबल लिसनर या क्लोज़र अभी भी इसे बनाए रख सकता है → Retainers को रूट तक फ़ॉलो करें और वास्तविक ओनर को रिलीज़ करें।
- हर संरचना को WeakMap या WeakRef से बदलना → अन्य मजबूत रेफरेंस अभी भी ऑब्जेक्ट्स को रीचेबल बनाते हैं → पहले अवांछित लिसनर्स, सब्सक्रिप्शन, टाइमर और कैश रेफरेंस हटाएं।
- केवल अनमाउंट के बाद setState को रोकना → बाहरी रिसोर्स अभी भी कॉलबैक और ऑब्जेक्ट ग्राफ़ को बनाए रख सकते हैं → कार्य को रद्द करें और इसे अनरजिस्टर करें।
- केवल यह सत्यापित करना कि पेज क्लिक करने योग्य बना हुआ है → कार्यात्मक शुद्धता रिलीज़ को साबित नहीं करती है → समान स्नैपशॉट प्रयोग को दोहराएं और लाइव इंस्टेंसेस, बेसलाइन्स और रिटेनिंग पाथ्स की तुलना करें।
फ़ॉलो-अप प्रश्न और उत्तर
फ़ॉलो-अप 1: सर्कुलर रेफरेंस से अनिवार्य रूप से लीक क्यों नहीं होता है?
मार्क-एंड-स्वीप पूछता है कि क्या कोई रूट ऑब्जेक्ट्स तक पहुंच सकता है। दो ऑब्जेक्ट एक-दूसरे को संदर्भित कर सकते हैं और फिर भी कलेक्ट किए जा सकते हैं जब कोई रीचेबल बाहरी पाथ उस समूह की ओर इशारा नहीं करता है। यदि कोई window लिसनर, मॉड्यूल कैश, या सक्रिय टाइमर उनमें से किसी एक तक पहुंचता है, तो पूरा समूह रीचेबल बना रहता है।
फ़ॉलो-अप 2: हटाया गया DOM नोड स्नैपशॉट में क्यों रह सकता है?
हटाने से नोड डॉक्यूमेंट ट्री से अलग हो जाता है। एक JavaScript वेरिएबल, लिसनर कॉलबैक, थर्ड-पार्टी कंपोनेंट, या ऑब्ज़र्वर अभी भी इसे संदर्भित कर सकता है। डिटेच्ड नोड के Retainers का निरीक्षण करें, पाथ के साथ लाइव ओनर का पता लगाएं, और उस ओनर के रिलीज़ ऑपरेशन को इनवोक करें।
फ़ॉलो-अप 3: आप Heap Snapshot, Allocation timeline, या sampling कब चुनते हैं?
स्नैपशॉट स्थिर बिंदुओं पर लाइव ऑब्जेक्ट्स की तुलना करते हैं और प्रति-इंस्टेंस रिटेनिंग पाथ को उजागर करते हैं। Allocation timeline किसी यूज़र एक्शन को उन एलोकेशन्स से जोड़ती है जो बाद में लाइव रहते हैं। Sampling कम ओवरहेड के साथ भारी एलोकेशन के लिए जिम्मेदार फ़ंक्शंस ढूंढती है। एक व्यावहारिक क्रम पहले कर्व को साबित करता है, ऑब्जेक्ट्स और रेफरेंस के लिए स्नैपशॉट का उपयोग करता है, फिर स्रोत अस्पष्ट होने पर एलोकेशन रिकॉर्डिंग जोड़ता है।
फ़ॉलो-अप 4: क्या "मेमोरी 10 MB से कम बढ़नी चाहिए" एक स्वचालित गेट हो सकता है?
एक निश्चित पूर्ण मान डिवाइस, ब्राउज़र, बिल्ड, डेटा और GC समय के प्रति संवेदनशील होता है। एक मजबूत गेट पर्यावरण और एक्शन को स्थिर करता है, वार्म-अप करता है, समूहों को दोहराता है, और पोस्ट-GC बेसलाइन स्लोप, नियंत्रित कंस्ट्रक्टर्स की लाइव संख्या और DOM/लिसनर काउंट की जांच करता है। स्पष्ट नॉइज़ टॉलरेंस के साथ उस पेज के ऐतिहासिक वितरण और प्रोडक्ट बजट से सीमाएं निर्धारित करें।
फ़ॉलो-अप 5: क्या होगा यदि पेज छिपा हुआ है लेकिन कभी अनमाउंट नहीं होता है?
प्रोडक्ट अनुबंध को स्पष्ट करें। जानबूझकर कीप-अलाइव व्यवहार के लिए, टाइमर रोकें, सैंपलिंग कम करें, या अदृश्य सब्सक्रिप्शन बंद करें और उन्हें बाद में फिर से शुरू करें; फिर भी कैश और इंस्टेंस काउंट को सीमित करें। "लिस्ट पर लौटने के बाद शून्य" अब लागू नहीं होता है। स्वीकृति के लिए स्थिर, बाउंडेड उपयोग और कंपोनेंट के वास्तव में नष्ट होने पर पूर्ण रिलीज़ की आवश्यकता होनी चाहिए।