Google Chat ने एजेंट के लिए Chrome DevTools की मदद से, मेमोरी से जुड़ी 30 से ज़्यादा समस्याओं को कैसे ठीक किया

Vatan Aggarwal
Vatan Aggarwal Software Engineer

पब्लिश होने की तारीख: 29 अक्टूबर, 2026

Google Chat, एक पेज का बड़ा ऐप्लिकेशन है. उपयोगकर्ता अक्सर वेब ऐप्लिकेशन को अपने ब्राउज़र टैब पर कई दिनों या हफ़्तों तक खुला रखते हैं. लंबे समय तक चलने वाले ऐप्लिकेशन में, क्लाइंट-साइड मेमोरी की परफ़ॉर्मेंस सबसे अहम होती है. अनकलेक्टेड क्लोज़र और डिटैच्ड डीओएम ट्री धीरे-धीरे बनते हैं. इससे यूज़र इंटरफ़ेस (यूआई) की परफ़ॉर्मेंस पर असर पड़ता है. साथ ही, कम मेमोरी वाले डिवाइसों पर, गार्बेज कलेक्शन रुक जाता है या टैब क्रैश हो जाते हैं.

इस समस्या को हल करने के लिए, Google Chat की टीम ने मेमोरी से जुड़ी समस्याओं का पता लगाने और उन्हें ठीक करने की पहल शुरू की. उन्होंने एजेंट के लिए Chrome DevTools का इस्तेमाल किया. इसमें DevTools MCP सर्वर शामिल है. इसकी मदद से, मैन्युअल तरीके से की जाने वाली मुश्किल प्रोसेस को ऑटोमेटेड वर्कफ़्लो में बदला गया.

चुनौती: 500 एमबी के डेटा में से काम की जानकारी ढूंढना

लीक हुए ऑब्जेक्ट को मेमोरी में क्यों रखा गया है, इसकी सटीक वजह जानने के लिए, बहुत ज़्यादा कम किए गए कोड के ज़रिए पाथ को ट्रेस करना ज़रूरी है. Chrome DevTools के मेमोरी पैनल में हाल ही में किए गए सुधारों (जैसे, ऐडवांस एज फ़िल्टरिंग और ऑब्जेक्ट क्वेरी) से यह काम आसान हो गया है. हालांकि, 500 एमबी के स्नैपशॉट पर इसे मैन्युअल तरीके से करना अब भी धीमा और थका देने वाला हो सकता है.

लीक को ठीक करने के लिए, दो अलग-अलग तरह की जानकारी की ज़रूरत होती है:

  • आपको यह समझना होगा कि V8 JavaScript इंजन, कंपाइल किए गए बाइट कोड को कैसे मैनेज करता है.
  • आपको अपने ऐप्लिकेशन के खास स्टेट मैनेजमेंट और कॉम्पोनेंट लाइफ़साइकल को भी समझना होगा.

जानकारी के इन दो अलग-अलग सेट को मैन्युअल तरीके से जोड़ने में, आम तौर पर कई दिनों की जांच और हर लीक के लिए कई डेवलपर के साथ मिलकर काम करना पड़ता है.

समाधान: एजेंट की मदद से हीप को नेविगेट करना

टीम को एहसास हुआ कि लार्ज लैंग्वेज मॉडल, स्ट्रक्चर्ड डेटा पर गहराई से विश्लेषण करने में बहुत अच्छे हैं. हालांकि, उन्हें बड़ी फ़ाइलों को प्रोसेस करने के लिए सही टूल की ज़रूरत होती है. साथ ही, उन्हें टोकन के इस्तेमाल को भी सीमित रखना होता है. Google Antigravity को ओपन-सोर्स Chrome DevTools MCP सर्वर से कनेक्ट करके, उन्होंने एजेंट को V8 हीप का डायरेक्ट प्रोग्रामैटिक ऐक्सेस दिया. इस सर्वर में मेमोरी टूल (--memoryDebugging=true) चालू थे.

डेवलपर को Chrome DevTools के मेमोरी पैनल में, रिटेनर ट्री को मैन्युअल तरीके से बड़ा करने की ज़रूरत नहीं पड़ी. इसके बजाय, एजेंट ने सीधे स्नैपशॉट से क्वेरी की. टीम ने प्रोसेस शुरू करने के लिए, एक आसान प्रॉम्प्ट का इस्तेमाल किया:

Discover memory leaks in the heap snapshot chat.heapsnapshot using Chrome DevTools memory tools.

मेमोरी डीबगिंग स्किल का इस्तेमाल करके, एजेंट ने एमसीपी टूल का इस्तेमाल करके एक स्ट्रक्चर्ड वर्कफ़्लो को लागू किया:

  1. स्नैपशॉट कैप्चर करना या लोड करना: take_heapsnapshot जैसे टूल का इस्तेमाल करके या किसी मौजूदा फ़ाइल को पढ़कर.
  2. हीप स्ट्रक्चर का विश्लेषण करना: इसने get_heapsnapshot_details और compare_heapsnapshots का इस्तेमाल करके, लीक के सामान्य सोर्स को स्कैन किया.
  3. पाथ बनाए रखने की जानकारी: इसने get_heapsnapshot_retaining_paths का इस्तेमाल करके, डॉमिनेटर ट्री को वापस गार्बेज कलेक्शन रूट पर भेजा.
  4. समस्याओं को ठीक करने के लिए कोड जनरेट करना: इसने ऐप्लिकेशन के सोर्स कोड के साथ, मेमोरी में मौजूद पाथ को क्रॉस-रेफ़रंस किया, ताकि समस्याओं को ठीक करने के लिए सटीक कोड जनरेट किए जा सकें.

तकनीकी जानकारी: मेमोरी लीक के तीन सामान्य पैटर्न

एजेंट ने 30 से ज़्यादा अलग-अलग लीक का पता लगाया. एजेंट को सबसे ज़्यादा असरदार तीन पैटर्न मिले. इनसे मेमोरी की सबसे ज़्यादा बचत होती है. साथ ही, ये मॉडर्न वेब ऐप्लिकेशन में सबसे आम आर्किटेक्चरल ऐंटी-पैटर्न हैं.

स्टैटिक गड़बड़ी का स्टैक ट्रेस ट्रैप

एजेंट को पता चला कि सबसे बड़ा लीक, पहले से तय किए गए गड़बड़ी वाले ऑब्जेक्ट से जुड़ा था. ये ऑब्जेक्ट, करीब 20 एमबी मेमोरी को हमेशा के लिए इस्तेमाल कर रहे थे. उदाहरण के लिए, एक स्टैटिक एरर कॉन्स्टेंट (EMPTY_KEY_ERROR) ने अपने-आप 14.1 एमबी मेमोरी का इस्तेमाल किया.

समस्या: V8 जैसे मॉडर्न JavaScript इंजन में, Error ऑब्जेक्ट बनाने से मौजूदा कॉल स्टैक का स्नैपशॉट कैप्चर हो जाता है. इससे डीबग करने में मदद मिलती है. इस तरीके की वजह से, रिटेन किया गया साइज़ बहुत ज़्यादा हो गया:

  • गड़बड़ी एक ग्लोबल कॉन्सटेंट थी: इसे शुरुआती मॉड्यूल के आकलन के दौरान इंस्टैंशिएट किया गया था.
  • JavaScript इंजन ने स्टैक को कैप्चर किया: उस समय स्टैक पर ऐक्टिव फ़ंक्शन, ऐप्लिकेशन का मुख्य इनिशलाइज़ेशन रैपर (इमीडिएटली इन्वोक फ़ंक्शन एक्सप्रेशन या IIFE) था.
  • अरे ने मेमोरी को ट्रैप कर लिया: ऐप्लिकेशन ने गड़बड़ी को कभी भी गार्बेज कलेक्ट नहीं किया. इस वजह से, इसके इंटरनल स्टैक ट्रेस अरे ने मुख्य बंडल के शुरू करने वाले फ़ंक्शन का स्थायी रेफ़रंस रखा.
// Ineffective pattern: Instantiating Error objects at module evaluation time
export const EMPTY_KEY_ERROR = new Error("Key cannot be empty");

export function validateKey(key: string) {
  if (!key) {
    throw EMPTY_KEY_ERROR;
  }
}

समस्या का हल: टीम ने कोड में बदलाव किया, ताकि गड़बड़ियां होने पर उन्हें डाइनैमिक तरीके से जनरेट किया जा सके. अगर फिर से इस्तेमाल की जा सकने वाली स्टैटिक गड़बड़ी दिखाना ज़रूरी है, तो स्टैक (EMPTY_KEY_ERROR.stack = '';) को साफ़ करने से, कैप्चर किए गए V8 इंटरनल फ़्रेम तुरंत खाली हो जाते हैं.

// Recommended: Construct errors dynamically
export function validateKey(key: string) {
  if (!key) {
    throw new Error("Key cannot be empty");
  }
}

// Or clear the stack property if a static instance is necessary:
export const REUSABLE_ERROR = new Error("Static error");
REUSABLE_ERROR.stack = ""; // Releases the captured initialization frame

ग्लोबल विंडो इवेंट लिसनर को स्केल करना

एजेंट को मेमोरी स्केलिंग से जुड़ी एक समस्या मिली. इसमें visibilitychange इवेंट लिसनर शामिल हैं. हालांकि, यह पूरी तरह से "लीक" नहीं है, क्योंकि कॉम्पोनेंट के अनमाउंट होने पर इन्हें हटा दिया जाता है. फिर भी, इससे मेमोरी का इस्तेमाल बढ़ गया, क्योंकि स्क्रीन पर मौजूद हर मैसेज के लिए एक लिसनर था. स्नैपशॉट से पता चला कि 1,142 बिल्ट-इन इवेंट लिसनर हैं. इनमें से 128 ऐक्टिव लिसनर, सीधे ग्लोबल विंडो ऑब्जेक्ट से जुड़े हैं.

समस्या: JavaScript में, इवेंट लिसनर कॉलबैक क्लोज़र होते हैं. क्लोज़र, अपने लेक्सिकल स्कोप के रेफ़रंस को बनाए रखता है. इससे यह पक्का होता है कि जिन वैरिएबल की उसे ज़रूरत है वे ऐक्सेस किए जा सकें. एजेंट ने इनमें से 126 श्रोताओं को ट्रैक किया और पाया कि वे सभी एक ही कॉम्पोनेंट से आए थे: RelativeTimeController. चैट के यूज़र इंटरफ़ेस (यूआई) में मौजूद हर टाइमस्टैंप (उदाहरण के लिए, "पांच मिनट पहले") अपना कंट्रोलर खुद मैनेज करता था. यह कंट्रोलर, समय को अपडेट करने के लिए एक प्राइवेट विज़िबिलिटी लिसनर को रजिस्टर करता था. हर लिसनर ने अपने कंट्रोलर इंस्टेंस को विंडो रूट से ऐंकर किया था. इस वजह से, ऐक्टिव क्लोज़र के एक डेंस वेब ने चैट थ्रेड के दिखने तक, काफ़ी मेमोरी का इस्तेमाल किया.

// Ineffective pattern: Attaching global window listeners in individual UI controllers
export class RelativeTimeController {
  constructor() {
    this.visibilityListener = () => this.updateTime();
    window.addEventListener("visibilitychange", this.visibilityListener);
  }

  // If dispose() fails to run or unregister, the closure pins the controller
}

समस्या का समाधान: टीम ने ग्लोबल विंडो इवेंट को एक जगह पर इकट्ठा किया. शेयर किया गया TimerService अब विंडो पर सिर्फ़ एक विज़िबिलिटी लिसनर को रजिस्टर करता है और सभी चालू टाइमस्टैंप कंट्रोलर को अपडेट ब्रॉडकास्ट करता है. इससे 126 श्रोताओं की संख्या तुरंत घटकर सिर्फ़ एक रह गई.

// Recommended: Centralized event coordination using a shared service
export class VisibilityService {
  private listeners = new Set<() => void>();

  constructor() {
    // Single global listener throttled for the entire application
    window.addEventListener("visibilitychange", () => this.notifyAll());
  }

  subscribe(callback: () => void): () => void {
    this.listeners.add(callback);
    return () => this.listeners.delete(callback); // Unsubscribe handle
  }
}

तीसरे पक्ष के एसडीके और एम्बेड किए गए iframe के लिए, डेटा को बनाए रखने की अवधि

अलग किए गए डीओएम नोड ऐसे एलिमेंट होते हैं जिन्हें दस्तावेज़ ट्री से हटा दिया गया है, लेकिन उन्हें अब भी JavaScript वैरिएबल से रेफ़र किया जाता है. अगर डिटैच किए गए डीओएम ट्री में मौजूद किसी एक एलिमेंट को रूट वैरिएबल की मदद से सेव किया जाता है, तो डिटैच किए गए पूरे ट्री को फिर से हासिल नहीं किया जा सकता.

समस्या: जब उपयोगकर्ता एक चैट स्पेस से दूसरे चैट स्पेस पर जाते थे, तो एजेंट को पता चलता था कि अलग किए गए पूरे डीओएम ट्री, मेमोरी में बने रहते थे. इसने चैट स्पेस के यूज़र इंटरफ़ेस (यूआई) में एम्बेड किए गए मीडिया प्लेयर को ट्रेस किया. जब चैट स्पेस को अनमाउंट किया जाता था, तो यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट को पेज से हटा दिया जाता था. हालांकि, मीडिया प्लेयर के एसडीके को कभी भी साफ़ तौर पर बंद नहीं किया जाता था. तीसरे पक्ष की लाइब्रेरी ने ऐक्टिव iframe आईडी की एक इंटरनल ग्लोबल डिक्शनरी बनाई, जिससे डिटैच किया गया डीओएम चालू रहा.

समस्या को ठीक करने का तरीका: पक्का करें कि एम्बेड किए गए सभी iframe और तीसरे पक्ष के विजेट, कॉम्पोनेंट डिस्पोज़ल लाइफ़साइकल से जुड़े हों. प्लेयर पर .destroy() तरीके को साफ़ तौर पर कॉल करने से, ग्लोबल एसडीके रजिस्ट्री से एंट्री मिट जाती है. साथ ही, डीओएम को गार्बेज कलेक्शन की अनुमति मिल जाती है.

// Recommended: Explicit teardown of embedded frames and SDK instances
export class MediaPlayerController {
  private player: ThirdPartyPlayer | null = null;

  dispose(): void {
    if (this.player) {
      this.player.destroy(); // Clears entry from global SDK registry
      this.player = null;
    }
  }
}

अन्य पैटर्न

ऊपर बताए गए तीन पैटर्न, सेव किए गए रॉ मेगाबाइट के मामले में सबसे ज़्यादा असरदार थे. हालांकि, एजेंट ने 27 से ज़्यादा अन्य लीक का भी पता लगाया और उन्हें ठीक किया. इनमें ये शामिल हैं:

  • इमेज कलेक्शन टेलीमेट्री: इसमें, बिना स्क्रोल किए दिखने वाले हिस्से में मौजूद इमेज की मेट्रिक को ट्रैक करने वाले कस्टम डाइग्नोस्टिक कलेक्शन की मदद से, डिटैच किए गए डीओएम ट्री को चालू रखा गया था
  • फ़्रेमवर्क कॉम्पोनेंट की लाइफ़साइकल से जुड़ी गड़बड़ियां: जहां दस्तावेज़ के मुख्य हिस्से में होस्ट किए गए कस्टम ड्रॉप-डाउन मेन्यू, अपने ओरिजनल पैरंट को अनमाउंट किए बिना ही बायपास कर देते हैं
  • इंप्लिसिट आर्ग्युमेंट ऑब्जेक्ट क्लोज़र ट्रैप: जहां इनर क्लोज़र, अनजाने में आउटर फ़ंक्शन के पूरे आर्ग्युमेंट कलेक्शन को कैप्चर कर लेते हैं

असर: वर्कफ़्लो के तौर पर प्रोऐक्टिव मेमोरी मैनेजमेंट

Google Chat की टीम ने कोडिंग एजेंट को Chrome DevTools for agents से लैस करके, एक बेहद खास टास्क को ऑटोमेट किया. मेमोरी लीक की जटिल समस्या का पता लगाने में, पहले कई दिन लगते थे. ऐसा इसलिए, क्योंकि हीप को मैन्युअल तरीके से ट्रैवर्स करना पड़ता था. अब ऑटोमेटेड एनालिसिस की मदद से, इस समस्या का पता मिनटों में लगाया जा सकता है.

यूज़र इंटरफ़ेस (यूआई) को स्टेबल रखने के लिए, P2ER ने "अनुभव के आधार पर पुष्टि करना ज़रूरी है" को लागू किया. इसी तरह, टीमें मेमोरी ऑडिट करने के इस तरीके को अपना सकती हैं. इससे, मेमोरी से जुड़ी समस्याओं को पहले ही रोका जा सकता है. डेवलपर अक्सर मेमोरी से जुड़ी समस्याओं को खारिज कर देते हैं. उन्हें लगता है कि ये समस्याएं सिर्फ़ बहुत बड़े या जटिल ऐप्लिकेशन में होती हैं. हालांकि, जैसा कि इस केस स्टडी से पता चलता है, स्टैंडर्ड इवेंट लिसनर या स्टैटिक गड़बड़ियों जैसे सामान्य पैटर्न, चुपचाप इकट्ठा हो सकते हैं. एजेंट के लिए Chrome DevTools जैसे टूल को अपने रोज़ के डेवलपमेंट साइकल में इंटिग्रेट करने से, एआई एक बुनियादी कोड जनरेटर से एक भरोसेमंद क्वालिटी गेट में बदल जाता है. इससे, प्रोडक्शन में जाने से पहले ही, बिना किसी सूचना के होने वाले रिग्रेशन का पता चल जाता है.