Kodokon kodokon.com

मेमोरी और परफ़ॉर्मेंस: GC, WeakMap/WeakRef, debounce/throttle

समझें कि गार्बेज कलेक्टर कैसे काम करता है, लीक से बचने के लिए वीक रेफरेंस का उपयोग करें, और debounce तथा throttle के साथ महँगे काम को सुचारू बनाएँ।

11 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

V8 का गार्बेज कलेक्टर पहुँच-योग्यता (reachability) पर आधारित है: कोई वस्तु तभी संग्रहणीय बन जाती है जैसे ही संदर्भों की कोई श्रृंखला उसे रूट्स (स्टैक, सक्रिय स्कोप, ग्लोबल्स) से नहीं जोड़ती। GC जनरेशनल है: नई वस्तुएँ *nursery* में रहती हैं, जिन्हें बार-बार और तेज़ी से संग्रहित किया जाता है (scavenge); बचे हुए को पुरानी पीढ़ी में पदोन्नत किया जाता है, जिसे इंक्रीमेंटल mark-and-sweep संभालता है। शास्त्रीय लीक GC से नहीं आतीं बल्कि उन संदर्भों से आती हैं जिन्हें आप भूल गए: Map-आधारित कैश, वे लिसनर जो कभी अलग नहीं किए जाते, बड़ी वस्तुओं को कैप्चर करने वाले क्लोज़र।

JAVASCRIPT
const strongCache = new Map();
const weakCache = new WeakMap();

let session = { user: "ada" };
strongCache.set(session, "data");
weakCache.set(session, "data");

session = null;
// Map: the key retains the object -> memory leak
// WeakMap: the entry becomes collectable
एक ही कोड, दो विपरीत मेमोरी नियतियाँ।

एक WeakMap अपनी कुंजियों को नहीं रोकता: जब कोई कुंजी कहीं और से पहुँच से बाहर हो जाती है, तो पूरी प्रविष्टि गायब हो जाती है। डिज़ाइन परिणाम: कुंजियाँ वस्तुएँ (या अपंजीकृत सिंबल) होनी चाहिए, और यह संरचना न तो इटरेबल है और न ही मापनीय - इसका आकार उजागर करना GC के व्यवहार को प्रकट कर देगा, जो नियतात्मक नहीं है। WeakRef और आगे जाता है: यह आपको एक सीधा वीक रेफरेंस देता है, जिसे deref() से डिरेफरेंस किया जाना चाहिए।

JAVASCRIPT
let config = { theme: "dark" };
const ref = new WeakRef(config);

function readTheme() {
  const target = ref.deref();
  return target ? target.theme : "default";
}

console.log(readTheme()); // "dark"
config = null;
// After a GC pass, deref() may
// return undefined: plan a fallback.
deref() वस्तु लौटा सकता है... या undefined।

अनुभूत-परफ़ॉर्मेंस की तरफ, उच्च-आवृत्ति वाले इवेंट (scroll, resize, input, mousemove) प्रति सेकंड सैकड़ों कॉल ट्रिगर कर सकते हैं। दो पूरक रणनीतियाँ: debounce फ़ंक्शन को केवल एक शांत अवधि के बाद चलाता है (टाइप-करते-समय-खोज के लिए आदर्श), जबकि throttle प्रति अंतराल अधिकतम एक निष्पादन की गारंटी देता है (स्क्रॉलिंग को ट्रैक करने के लिए आदर्श)।

JAVASCRIPT
function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(
      () => fn.apply(this, args),
      delay
    );
  };
}
Debounce: किसी बर्स्ट की केवल अंतिम कॉल गिनी जाती है।
JAVASCRIPT
function throttle(fn, interval) {
  let last = 0;
  return function (...args) {
    const now = Date.now();
    if (now - last >= interval) {
      last = now;
      fn.apply(this, args);
    }
  };
}
Throttle: प्रति अंतराल अधिकतम एक निष्पादन।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. WeakMap की कुंजियाँ स्ट्रिंग जैसे प्रिमिटिव के बजाय वस्तुएँ क्यों होनी चाहिए?
    • हैशिंग परफ़ॉर्मेंस के कारणों से
    • क्योंकि केवल किसी वस्तु का संदर्भ ही पहुँच से बाहर हो सकता है और प्रविष्टि को हटाना ट्रिगर कर सकता है
    • यह एक ऐतिहासिक सीमा है जिसे ES2021 से हटा दिया गया है
    • क्योंकि JavaScript में प्रिमिटिव को कुंजी के रूप में उपयोग नहीं किया जा सकता
  2. debounce और throttle के बीच मूलभूत अंतर क्या है?
    • Debounce एक शांत अवधि के बाद चलता है, throttle प्रति अंतराल अधिकतम एक निष्पादन की गारंटी देता है
    • Debounce अतुल्यकालिक है, throttle तुल्यकालिक है
    • Throttle पिछली कॉलों को रद्द कर देता है, debounce उन्हें कतार में लगा देता है
    • ये एक ही तकनीक के दो नाम हैं
  3. किसी WeakRef पर ref.deref() क्या लौटा सकता है?
    • हमेशा मूल वस्तु, जब तक WeakRef मौजूद है
    • वस्तु यदि वह अभी भी जीवित है, या undefined यदि वह संग्रहित हो चुकी है
    • वस्तु की एक गहरी प्रति
    • null यदि वस्तु संग्रहित हो चुकी है