Dipublikasikan: 29 Oktober 2026
Google Chat adalah aplikasi web satu halaman yang besar. Pengguna sering membiarkan aplikasi web terbuka di tab browser mereka selama berhari-hari atau berminggu-minggu. Dalam aplikasi yang berjalan lama, kesehatan memori sisi klien adalah masalah utama. Penutupan yang tidak dikumpulkan dan hierarki DOM yang terlepas akan menumpuk secara perlahan, yang pada akhirnya memengaruhi performa UI, jeda pembersihan sampah memori, atau error tab pada perangkat yang terbatas.
Untuk mengatasinya, tim Google Chat meluncurkan inisiatif untuk menemukan dan memperbaiki masalah memori. Mereka menggunakan Chrome DevTools untuk agen, yang mencakup server MCP DevTools, untuk mengubah proses manual yang sulit menjadi alur kerja otomatis.
Tantangan: Mencari jarum dalam tumpukan jerami berukuran 500 MB
Mengidentifikasi secara pasti alasan objek yang bocor disimpan dalam memori memerlukan pelacakan jalur retensi melalui kode yang sangat diminifikasi. Meskipun peningkatan terbaru pada panel Memori Chrome DevTools (misalnya, pemfilteran edge lanjutan dan kueri objek) mempermudah hal ini, melakukannya secara manual di seluruh snapshot 500 MB masih bisa lambat dan melelahkan secara mental.
Dan memperbaiki kebocoran memerlukan dua set pengetahuan yang berbeda:
- Anda perlu memahami cara mesin JavaScript V8 menangani bytecode yang dikompilasi.
- Anda juga perlu memahami pengelolaan status dan siklus proses komponen khusus aplikasi Anda.
Menjembatani kedua set pengetahuan yang berbeda ini secara manual biasanya memerlukan waktu berhari-hari untuk investigasi dan kolaborasi dengan beberapa developer per kebocoran.
Solusi: Menjelajahi heap dengan agen
Tim menyadari bahwa meskipun model bahasa besar sangat baik dalam melakukan penalaran
atas data terstruktur, model tersebut memerlukan alat yang tepat untuk memproses file besar sekaligus
menjaga konsumsi token tetap wajar. Dengan menghubungkan Google
Antigravity ke server MCP Chrome DevTools open source
dengan alat memori yang diaktifkan (--memoryDebugging=true), mereka memberi agen
akses terprogram langsung ke heap V8.
Alih-alih developer memperluas hierarki penahan secara manual di panel Memori Chrome DevTools, agen mengkueri snapshot secara langsung. Tim menggunakan perintah sederhana untuk memulai proses:
Discover memory leaks in the heap snapshot chat.heapsnapshot using Chrome DevTools memory tools.
Dengan menggunakan Kemampuan Proses Debug Memori, agen menjalankan alur kerja terstruktur menggunakan alat MCP tertentu:
- Mengambil atau memuat snapshot: Menggunakan alat seperti
take_heapsnapshotatau membaca file yang ada. - Menganalisis struktur heap: Alat ini memindai sumber kebocoran umum menggunakan
get_heapsnapshot_detailsdancompare_heapsnapshots. - Melacak jalur penahanan: Alat ini menggunakan
get_heapsnapshot_retaining_pathsuntuk menelusuri hierarki dominator kembali ke root pembersihan sampah memori. - Membuat perbaikan: Gemini mereferensi silang jalur penahan dengan kode sumber aplikasi untuk menghasilkan patch yang sangat spesifik.
Insight teknis: Tiga pola kebocoran memori yang umum
Agen mengidentifikasi lebih dari 30 kebocoran yang berbeda. Tiga pola berikut adalah pola yang paling berdampak yang ditemukan agen, yang mewakili penghematan memori terbesar dan anti-pola arsitektur yang paling umum dalam aplikasi web modern.
Jebakan stack trace error statis
Agen mengidentifikasi kebocoran tunggal terbesar terkait dengan objek error yang dialokasikan sebelumnya yang secara permanen mempertahankan hampir 20 MB memori. Misalnya, satu
konstanta error statis (EMPTY_KEY_ERROR) mempertahankan 14,1 MB dengan sendirinya.
Masalahnya: Di mesin JavaScript modern seperti V8, pembuatan objek Error
akan mengambil snapshot stack panggilan saat ini untuk membantu proses debug. Retained size yang sangat besar terjadi karena mekanisme ini:
- Error tersebut adalah konstanta global: Error tersebut dibuat selama evaluasi modul awal.
- Mesin JavaScript menangkap stack: Fungsi aktif pada stack pada saat itu adalah wrapper inisialisasi utama aplikasi (Immediately Invoked Function Expression, atau IIFE).
- Array menjebak memori: Karena aplikasi tidak pernah melakukan pembersihan sampah memori pada error, array pelacakan tumpukan internalnya menyimpan referensi permanen ke fungsi inisialisasi paket utama.
// 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;
}
}
Perbaikan: Tim mengubah kode untuk membuat error secara dinamis saat ditampilkan.
Jika error statis yang dapat digunakan kembali benar-benar diperlukan, menghapus stack secara eksplisit
(EMPTY_KEY_ERROR.stack = '';) akan segera mengosongkan frame internal V8
yang diambil.
// 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
Menskalakan pemroses peristiwa jendela global
Agen menemukan masalah penskalaan memori yang melibatkan pemroses peristiwa visibilitychange. Meskipun ini bukan "kebocoran" yang ketat (karena pada akhirnya akan dibersihkan
saat komponen dilepas), hal ini meningkatkan penggunaan memori karena ada satu
pemroses untuk setiap pesan di layar. Snapshot tersebut mengungkapkan 1.142 pemroses peristiwa bawaan, dengan 128 pemroses aktif yang terpasang langsung ke objek jendela global.
Masalahnya: Di JavaScript, callback pemroses peristiwa adalah closure. Closure
mempertahankan referensi ke cakupan leksikal yang melingkupinya untuk memastikan variabel yang
dibutuhkan tetap dapat diakses. Agen melacak 126 pemroses ini kembali ke satu komponen: RelativeTimeController. Setiap stempel waktu di UI Chat
(misalnya, "5 menit yang lalu") mengelola pengontrolnya sendiri, yang mendaftarkan
pemroses visibilitas pribadi untuk memperbarui waktu. Karena setiap pemroses menambatkan instance pengontrolnya ke root jendela, jaringan closure aktif yang padat menghabiskan memori yang signifikan selama masa aktif rangkaian pesan chat yang terlihat.
// 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
}
Perbaikan: Tim memusatkan peristiwa jendela global. TimerService bersama
kini mendaftarkan tepat satu pemroses visibilitas di jendela dan menyiarkan
update ke semua pengontrol stempel waktu yang aktif. Tindakan ini langsung mengurangi 126 pemroses menjadi tepat 1.
// 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
}
}
SDK pihak ketiga dan penahan iframe tersemat
Node DOM terpisah adalah elemen yang telah dihapus dari hierarki dokumen, tetapi masih direferensikan oleh variabel JavaScript. Jika satu elemen dalam hierarki DOM terpisah dipertahankan oleh variabel root, seluruh hierarki terpisah tidak dapat diklaim kembali.
Masalah: Saat pengguna berpindah di antara ruang chat, agen menemukan bahwa seluruh hierarki DOM yang terlepas tetap berada di memori. Alat ini melacak jalur penahanan kembali ke pemutar media tersemat dalam UI ruang chat. Saat ruang chat dilepas, komponen UI dihapus dari halaman, tetapi SDK pemutar media yang mendasarinya tidak pernah dihancurkan secara eksplisit. Library pihak ketiga mempertahankan kamus global internal ID iframe aktif, sehingga DOM yang terpisah tetap aktif.
Perbaikan: Pastikan semua iframe tersemat dan widget pihak ketiga terkait dengan
siklus proses penghapusan komponen. Memanggil metode .destroy() secara eksplisit pada
pemutar akan menghapus entri dari registry SDK global dan memungkinkan DOM
dikumpulkan sebagai sampah.
// 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;
}
}
}
Pola tambahan
Meskipun tiga pola yang disebutkan sebelumnya adalah yang paling berdampak dalam hal penghematan megabyte mentah, agen juga mendeteksi dan memperbaiki lebih dari 27 kebocoran lainnya. Ini termasuk:
- Telemetri array gambar: Tempat hierarki DOM yang terlepas tetap aktif oleh array diagnostik kustom yang melacak metrik gambar paruh atas
- Bug siklus proses komponen framework: Tempat menu drop-down kustom yang diangkat ke isi dokumen mengabaikan pelepasan induk aslinya
- Perangkap penutupan objek argumen implisit: Di mana penutupan dalam secara tidak sengaja menangkap seluruh array argumen fungsi luar
Dampak: Pengelolaan memori proaktif sebagai alur kerja
Dengan melengkapi agen coding dengan Chrome DevTools untuk agen, tim Google Chat mengotomatiskan tugas yang sangat terspesialisasi. Pelacakan kebocoran memori yang kompleks berkurang dari berhari-hari penelusuran heap manual menjadi analisis otomatis dalam hitungan menit.
Sama seperti P2ER yang menerapkan "Verifikasi Empiris Wajib" untuk menerapkan stabilitas UI, tim dapat mengadopsi pendekatan audit memori ini sebagai batasan proaktif. Developer sering kali mengabaikan masalah memori, dengan asumsi bahwa masalah tersebut hanya terjadi pada aplikasi yang sangat besar atau kompleks. Namun, seperti yang ditunjukkan dalam studi kasus ini, pola umum seperti pemroses peristiwa standar atau error statis dapat terakumulasi secara diam-diam. Mengintegrasikan alat seperti Chrome DevTools untuk agen ke dalam siklus pengembangan harian Anda akan mengubah AI dari generator kode dasar menjadi quality gate yang andal yang mendeteksi regresi senyap sebelum mencapai produksi.