Node.js बैकएंड विकास बूटकैंप · पाठ

हीप स्नैपशॉट कैप्चर और तुलना

इंस्पेक्टर में हीप स्नैपशॉट का अंतर निकालकर रखे गए ऑब्जेक्ट और लीक के स्रोत खोजिए।

पाठ 2, कुल 4 में से13 चरण

हीप स्नैपशॉट कैप्चर और तुलना, CoddyKit पर Node.js बैकएंड विकास बूटकैंप का एक निःशुल्क पाठ है। यह 4 में से 2वाँ पाठ है। आप नीचे पूरा पाठ निःशुल्क पढ़ सकते हैं—फिर अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर के साथ ब्राउज़र में इसका व्यावहारिक अभ्यास कर सकते हैं। यह Node.js बैकएंड विकास बूटकैंप सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

हीप स्नैपशॉट क्यों महत्वपूर्ण हैं

हीप स्नैपशॉट उस क्षण V8 में जीवित प्रत्येक JavaScript ऑब्जेक्ट का पूरा डंप होता है, जब आप उसे कैप्चर करते हैं। Node.js बैकएंड के लिए यह एक प्रश्न का उत्तर पाने का सबसे सटीक साधन है: अभी क्या बनाए रखा जा रहा है और क्यों?

  • लीक करने वाली प्रक्रिया ऐसे ऑब्जेक्ट आवंटित करती रहती है जिन्हें कभी कचरा-संग्रहित नहीं किया जाता, क्योंकि कोई चीज़ अभी भी उनका संदर्भ पकड़े हुए है।
  • एक स्नैपशॉट बताता है कि अभी क्या मौजूद है; समय के साथ दो स्नैपशॉट की तुलना बताती है कि क्या बढ़ रहा है — यही वास्तविक लीक का संकेत है।

इस पाठ में आप चल रही Node सेवा से स्नैपशॉट कैप्चर करेंगे, उन्हें Chrome DevTools में लोड करेंगे, उनकी तुलना करेंगे और रिटेनर चेन को पढ़कर समस्या पैदा करने वाले कोड तक पहुँचेंगे।

इंस्पेक्टर को उपलब्ध कराना

किसी वास्तविक सेवा से स्नैपशॉट कैप्चर करने के लिए पहले V8 इंस्पेक्टर से जुड़ना आवश्यक है। प्रक्रिया को --inspect के साथ शुरू करें, ताकि DevTools (या inspector प्रोटोकॉल) उससे जुड़ सके।

  • node --inspect server.js इंस्पेक्टर को 127.0.0.1:9229 पर खोलता है।
  • Chrome में chrome://inspect खोलें, अपने लक्ष्य पर inspect पर क्लिक करें, फिर Memory टैब पर जाएँ।
  • उत्पादन वातावरण में इंस्पेक्टर को कभी भी सार्वजनिक इंटरफ़ेस से न बाँधें — इससे पूरा कोड चलाने का अधिकार मिल जाता है।

नीचे दिया गया अंश वही CLI कमांड है जिसे आप अपने प्रारंभ आदेश में स्क्रिप्ट के रूप में रखेंगे।

// package.json scripts
{
  "scripts": {
    "debug": "node --inspect=127.0.0.1:9229 server.js",
    "debug:brk": "node --inspect-brk server.js"
  }
}

प्रोग्राम के माध्यम से स्नैपशॉट कैप्चर करना

आप उत्पादन सर्वर पर हमेशा DevTools नहीं खोल सकते। अंतर्निहित v8 मॉड्यूल आवश्यकता पड़ने पर डिस्क पर .heapsnapshot फ़ाइल लिख सकता है, जिसे बाद में DevTools में ऑफ़लाइन लोड किया जा सकता है।

  • v8.writeHeapSnapshot(filename) पूरे हीप को समकालिक रूप से क्रमबद्ध करता है।
  • इसे किसी सिग्नल हैंडलर या आंतरिक व्यवस्थापक मार्ग से ट्रिगर करें, ताकि सेवा को पुनः आरंभ किए बिना स्नैपशॉट लिए जा सकें।

यह प्रोग्राम पूरी तरह स्वतंत्र है — यह एक स्नैपशॉट लिखकर बाहर निकल जाता है।

const v8 = require('v8');
const path = require('path');

function takeSnapshot(label) {
  const file = path.join(process.cwd(), `heap-${label}-${Date.now()}.heapsnapshot`);
  const written = v8.writeHeapSnapshot(file);
  console.log('Snapshot written to', written);
  return written;
}

takeSnapshot('baseline');

सिग्नल पर स्नैपशॉट ट्रिगर करना

उत्पादन वातावरण में एक सामान्य तरीका यह है कि OS सिग्नल के लिए सुनें और चल रही सेवा को छुए बिना स्नैपशॉट डंप करें। आप kill -USR2 <pid> भेजते हैं और प्रक्रिया एक फ़ाइल लिख देती है।

  • SIGUSR2 परंपरागत रूप से ऐप के लिए उपलब्ध रहता है (Nodemon इसका उपयोग पुनः आरंभ करने के लिए करता है, इसलिए यदि आप Nodemon चला रहे हैं तो कोई दूसरा सिग्नल चुनें)।
  • हमेशा GC को ध्यान में रखें: अगले GC तक स्नैपशॉट में पहुँच से बाहर ऑब्जेक्ट भी शामिल रहते हैं, इसलिए हीप के स्थिर हो जाने के बाद स्नैपशॉट लें।
const v8 = require('v8');

process.on('SIGUSR2', () => {
  const file = `heap-${process.pid}-${Date.now()}.heapsnapshot`;
  v8.writeHeapSnapshot(file);
  console.log('Heap snapshot captured:', file);
});

console.log('Send: kill -USR2', process.pid);
setInterval(() => {}, 1 << 30);

तीन-स्नैपशॉट तकनीक

एक स्नैपशॉट में बहुत-सी अनावश्यक जानकारी होती है — इसमें हर चीज़ शामिल रहती है, जिनमें सचमुच लंबे समय तक रहने वाले कैश भी हैं। लीक खोजने की प्रचलित विधि तीन-स्नैपशॉट तकनीक है:

  • स्नैपशॉट 1 — आधाररेखा, वार्म-अप के ठीक बाद।
  • संदिग्ध कोड पथ को कई बार चलाएँ (जैसे, किसी एंडपॉइंट को 1,000 बार अनुरोध भेजें)।
  • स्नैपशॉट 2 — कार्यभार के बाद।
  • कार्यभार फिर से चलाएँ, फिर स्नैपशॉट 3 लें।

जो ऑब्जेक्ट स्नैपशॉट 2 में दिखाई देते हैं और स्नैपशॉट 3 तक बने रहते हैं, वही वास्तविक लीक हैं — तब तक अस्थायी अनुरोध ऑब्जेक्ट संग्रहित हो चुके होंगे।

स्वच्छ स्नैपशॉट के लिए GC को बाध्य करना

DevTools प्रत्येक स्नैपशॉट से पहले अपने-आप पूर्ण GC चलाता है, इसलिए आपको केवल वास्तव में पहुँच योग्य मेमरी दिखाई देती है। प्रोग्राम के माध्यम से कैप्चर करते समय आपको भी ऐसा ही करना चाहिए, ताकि जल्द ही समाप्त होने वाले कचरे की गिनती न हो।

  • --expose-gc के साथ Node चलाएँ, ताकि global.gc() उपलब्ध हो जाए।
  • स्नैपशॉट लेने से पहले global.gc() को दो बार कॉल करें — दूसरा चरण उन ऑब्जेक्ट को साफ़ करता है जो पहले चरण के दौरान मुक्त हुए थे।
// run with: node --expose-gc snapshot.js
const v8 = require('v8');

function cleanSnapshot(label) {
  if (global.gc) {
    global.gc();
    global.gc();
  }
  return v8.writeHeapSnapshot(`heap-${label}.heapsnapshot`);
}

console.log(cleanSnapshot('after-gc'));

सारांश दृश्य पढ़ना

.heapsnapshot को DevTools के Memory टैब में लोड करें। डिफ़ॉल्ट सारांश दृश्य ऑब्जेक्ट को कंस्ट्रक्टर के आधार पर समूहित करता है।

  • ऑब्जेक्ट संख्या — उस कंस्ट्रक्टर के कितने जीवित इंस्टेंस मौजूद हैं।
  • प्रत्यक्ष आकार — ऑब्जेक्ट द्वारा स्वयं रखी गई मेमरी, उसके संदर्भित ऑब्जेक्ट को छोड़कर।
  • रक्षित आकार — यदि यह ऑब्जेक्ट हटा दिया जाए तो मुक्त होने वाली मेमरी, जिसमें वह सब भी शामिल है जिसे केवल यही जीवित रखता है। लीक के लिए यही संख्या महत्वपूर्ण है।

हीप पर सबसे अधिक प्रभाव डालने वाले ऑब्जेक्ट खोजने के लिए रक्षित आकार के आधार पर घटते क्रम में क्रमित करें।

तुलना दृश्य: दो स्नैपशॉट की तुलना

वास्तविक शक्ति तुलना दृश्य में है। दो स्नैपशॉट लोड करने के बाद ड्रॉपडाउन को सारांश से तुलना में बदलें और आधाररेखा को तुलना का आधार चुनें।

  • #नए — आधाररेखा के बाद आवंटित किए गए ऑब्जेक्ट।
  • #हटाए गए — आधाररेखा के बाद संग्रहित किए गए ऑब्जेक्ट।
  • #अंतर — शुद्ध परिवर्तन। ऐसा कंस्ट्रक्टर जिसका धनात्मक अंतर बड़ा हो और जो प्रत्येक तुलना में बढ़ता रहे, वही आपका लीक है।
  • आकार का अंतर — बनाए रखे गए बाइट में शुद्ध वृद्धि।

उन धनात्मक अंतरों पर ध्यान दें जिनकी संख्या आपके कार्यभार के प्रत्येक चक्र के साथ समान गति से बढ़ती है।

दोहराए जा सकने वाले लीक का निर्माण

इस प्रक्रिया का अभ्यास करने के लिए आपको एक नियतात्मक लीक चाहिए। मॉड्यूल-स्तर का ऐसा ऐरे (या Map) जिसमें आप लगातार push करते रहें और जिसे कभी साफ़ न करें, इसका मानक उदाहरण है। प्रत्येक अनुरोध ऐसा डेटा जोड़ता है जिसे कभी संग्रहित नहीं किया जा सकता।

  • leaked ऐरे मॉड्यूल के स्कोप से पहुँच योग्य है, इसलिए V8 को हर प्रविष्टि बनाए रखनी पड़ती है।
  • DevTools में यह बढ़ते हुए (array) या बढ़ते अंतर वाले आपके क्लोज़र कंस्ट्रक्टर के रूप में दिखाई देता है।

यह स्वतंत्र प्रोग्राम जानबूझकर लीक करता है और हीप की वृद्धि प्रिंट करता है।

const leaked = [];

function handleRequest(i) {
  // Bug: we never remove old entries
  leaked.push({ id: i, payload: 'x'.repeat(1024), ts: Date.now() });
}

for (let i = 0; i < 5000; i++) handleRequest(i);

const mb = process.memoryUsage().heapUsed / 1024 / 1024;
console.log('Entries retained:', leaked.length);
console.log('Heap used (MB):', mb.toFixed(1));

रिटेनर चेन का अनुसरण करना

संदिग्ध कंस्ट्रक्टर मिलने पर इंस्टेंस विस्तृत करने के लिए उस पर क्लिक करें, फिर नीचे दिए गए रिटेनर पैन को देखें। रिटेनर उस एक महत्वपूर्ण प्रश्न का उत्तर देते हैं: इस ऑब्जेक्ट को जीवित कौन रखे हुए है?

  • चेन ऑब्जेक्ट से ऊपर की ओर GC रूट तक जाती है (ग्लोबल ऑब्जेक्ट, मॉड्यूल क्लोज़र, अभी लंबित Promise, सक्रिय टाइमर आदि)।
  • पीले रंग से हाइलाइट किया गया नोड किसी JS चर से सीधे पहुँच योग्य होता है; लाल रंग अलग हो चुके DOM-जैसे नोड को दर्शाता है (Node में यह दुर्लभ है)।
  • चेन का तब तक अनुसरण करें, जब तक आपको अपना चर न मिल जाए — यही वह कोड पंक्ति है जिसे ठीक करना है।

बड़े स्कोप पर बने क्लोज़र, असीमित कैश और ऐसे EventEmitter लिस्नर जिन्हें कभी हटाया नहीं जाता, आम दोषी होते हैं।

const EventEmitter = require('events');
const bus = new EventEmitter();

// Leak: a new listener per call, never removed
function subscribe(userId) {
  const bigContext = { userId, cache: new Array(10000).fill(userId) };
  bus.on('tick', () => bigContext.cache[0]);
}

for (let i = 0; i < 200; i++) subscribe(i);
console.log('Listener count:', bus.listenerCount('tick'));

अंतिम तुलना से सुधार की पुष्टि करना

लीक ठीक करने के बाद उसी तीन-स्नैपशॉट प्रक्रिया से इसका प्रमाण दें। वही कार्यभार फिर चलाएँ और आधाररेखा की तुलना कार्यभार के बाद वाले स्नैपशॉट से करें।

  • पहले बढ़ रहा कंस्ट्रक्टर अब लगभग शून्य अंतर दिखाना चाहिए — आवंटन और संग्रहण का संतुलन हो जाना चाहिए।
  • WeakMap या सीमित LRU कैश का उपयोग करें, ताकि संदर्भ न रहने पर प्रविष्टियाँ GC के लिए योग्य हो जाएँ।
  • एक सुरक्षा जाँच स्वचालित करें: सोक परीक्षण में N चक्रों के दौरान GC के बाद heapUsed किसी सीमा से नीचे रहने की पुष्टि करें।
const cache = new WeakMap();

function attach(req) {
  // Keyed by the request object; entry is collectable once req is gone
  cache.set(req, { processedAt: Date.now() });
}

let req1 = { id: 1 };
attach(req1);
console.log('Has entry:', cache.has(req1));
req1 = null; // now eligible for GC; WeakMap will not retain it
console.log('Reference dropped — entry can be collected');

त्वरित जाँच

आप मानक लीक-खोज तकनीक का उपयोग करके तीन हीप स्नैपशॉट लेते हैं। कौन-से ऑब्जेक्ट वास्तविक मेमरी लीक का सबसे मजबूत प्रमाण हैं?

पुनरावलोकन

अब आपके पास Node.js बैकएंड के लिए हीप-स्नैपशॉट द्वारा लीक खोजने की पूरी प्रक्रिया है:

  • कैप्चर करें — --inspect + DevTools, v8.writeHeapSnapshot() या उत्पादन वातावरण में SIGUSR2 हैंडलर के माध्यम से स्नैपशॉट लें।
  • GC को बाध्य करें — (--expose-gc, दो बार global.gc()) ताकि स्नैपशॉट केवल पहुँच योग्य मेमरी दर्शाएँ।
  • वास्तविक लीक को अस्थायी आवंटन से अलग करने के लिए तीन-स्नैपशॉट तकनीक का उपयोग करें।
  • तुलना दृश्य में बढ़ती हुई #अंतर संख्याएँ खोजें; रक्षित आकार के आधार पर क्रमित करें।
  • दोषी सटीक चर खोजने के लिए रिटेनर चेन का GC रूट तक अनुसरण करें — आम तौर पर दोषी असीमित कैश, बना हुआ क्लोज़र या न हटाया गया लिस्नर होता है।
  • अंतर के स्थिर रहने वाली एक और तुलना से सुधार की पुष्टि करें; WeakMap या सीमित कैश को प्राथमिकता दें।
शुरुआत निःशुल्क

एआई शिक्षक के साथ JavaScript सीखें — निःशुल्क

अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।

पाठ्यक्रम
22
पाठ
92

अक्सर पूछे जाने वाले प्रश्न

क्या “हीप स्नैपशॉट कैप्चर और तुलना” पाठ निःशुल्क है?

हाँ—“हीप स्नैपशॉट कैप्चर और तुलना” का पूरा पाठ यहाँ वेब पर निःशुल्क पढ़ा जा सकता है। इंटरैक्टिव अभ्यास (अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर) करने और Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम का बाकी हिस्सा अनलॉक करने के लिए CoddyKit PRO लें। Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

“हीप स्नैपशॉट कैप्चर और तुलना” में मैं क्या सीखूँगा?

इंस्पेक्टर में हीप स्नैपशॉट का अंतर निकालकर रखे गए ऑब्जेक्ट और लीक के स्रोत खोजिए। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ Node.js बैकएंड विकास बूटकैंप का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।

क्या Node.js बैकएंड विकास बूटकैंप शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?

पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर Node.js बैकएंड विकास बूटकैंप शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 2वाँ पाठ है।

“हीप स्नैपशॉट कैप्चर और तुलना” पाठ पूरा करने में कितना समय लगता है?

CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।

क्या मैं इस Node.js बैकएंड विकास बूटकैंप पाठ में कोड लिख और चला सकता हूँ?

हाँ। हर Node.js बैकएंड विकास बूटकैंप पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।

इस पाठ्यक्रम के सभी पाठ

  1. V8 हीप, जनरेशनल GC और ऑब्जेक्ट का जीवनकाल
  2. हीप स्नैपशॉट कैप्चर और तुलना
  3. हॉट पथों के लिए CPU प्रोफ़ाइल और फ़्लेम ग्राफ़
  4. सामान्य लीक पैटर्न का पता लगाना और समाधान
← Node.js बैकएंड विकास बूटकैंप पर वापस जाएँ