Yayınlanma tarihi: 29 Ekim 2026
Google Chat, büyük bir tek sayfalık uygulamadır. Kullanıcılar web uygulamasını tarayıcı sekmelerinde günlerce veya haftalarca açık bırakıyor. Uzun ömürlü uygulamalarda istemci tarafı bellek sağlığı birincil öneme sahiptir. Toplanmayan kapatmalar ve ayrılmış DOM ağaçları yavaş yavaş birikir ve sonunda kullanıcı arayüzü performansını, çöp toplama duraklamalarını veya kısıtlı cihazlarda sekme kilitlenmelerini etkiler.
Google Chat ekibi, bu sorunu çözmek için bellek sorunlarını tespit edip düzeltmeye yönelik bir girişim başlattı. Zorlu bir manuel süreci otomatik bir iş akışına dönüştürmek için Geliştirici Araçları MCP sunucusunu içeren ajanlar için Chrome Geliştirici Araçları'nı kullandılar.
Zorluk: 500 MB'lık bir samanlıkta iğne aramak
Sızdırılan bir nesnenin neden bellekte tutulduğunu tam olarak belirlemek için, büyük ölçüde sadeleştirilmiş kod aracılığıyla tutma yollarının izlenmesi gerekir. Chrome Geliştirici Araçları Bellek panelinde yapılan son iyileştirmeler (ör. gelişmiş kenar filtreleme ve nesne sorgulama) bu işlemi kolaylaştırsa da 500 MB'lık bir anlık görüntüde manuel olarak yapmak yine de yavaş ve zihinsel olarak yorucu olabilir.
Sızıntıyı gidermek için iki farklı bilgi kümesi gerekir:
- V8 JavaScript motorunun, derlenmiş bayt kodunu nasıl işlediğini anlamanız gerekir.
- Ayrıca uygulamanızın özel durum yönetimini ve bileşen yaşam döngüsünü de anlamanız gerekir.
Bu iki farklı bilgi kümesini manuel olarak birleştirmek genellikle günler süren bir araştırma ve her sızıntı için birden fazla geliştiriciyle işbirliği yapmayı gerektirir.
Çözüm: Bir temsilciyle yığında gezinme
Ekip, büyük dil modellerinin yapılandırılmış veriler üzerinde akıl yürütme konusunda çok iyi olduğunu ancak devasa dosyaları işlerken jeton tüketimini makul düzeyde tutmak için doğru araçlara ihtiyaç duyduğunu fark etti. Google Antigravity'yi, bellek araçları etkinleştirilmiş (--memoryDebugging=true) açık kaynak Chrome Geliştirici Araçları MCP sunucusuna bağlayarak temsilciye V8 yığınına doğrudan programatik erişim sağladılar.
Temsilci, geliştiricinin Chrome Geliştirici Araçları Bellek panelinde tutucu ağaçlarını manuel olarak genişletmesi yerine doğrudan anlık görüntüyü sorguladı. Ekip, süreci başlatmak için basit bir istem kullandı:
Discover memory leaks in the heap snapshot chat.heapsnapshot using Chrome DevTools memory tools.
Temsilci, Bellek Hata Ayıklama Becerisi'ni kullanarak belirli MCP araçlarıyla yapılandırılmış bir iş akışı yürüttü:
- Anlık görüntüyü yakalama veya yükleme:
take_heapsnapshotgibi araçları kullanarak veya mevcut bir dosyayı okuyarak. - Yığın yapısını analiz etme:
get_heapsnapshot_detailsvecompare_heapsnapshotskullanarak yaygın sızıntı kaynaklarını taradı. - Yolları koruma izi: Çöp toplama köklerine geri dönmek için
get_heapsnapshot_retaining_pathskullanıldı. - Düzeltme oluşturma: Tutma yollarını uygulamanın kaynak koduyla karşılaştırarak son derece spesifik yamalar oluşturdu.
Teknik analizler: Üç yaygın bellek sızıntısı kalıbı
Temsilci, 30'dan fazla sızıntı tespit etti. Aşağıdaki üç kalıp, temsilcinin bulduğu en etkili kalıplardır. Bunlar, en büyük bellek tasarrufunu ve modern web uygulamalarındaki en yaygın mimari anti-kalıpları temsil eder.
Statik hata yığın izleme (stack trace) yakalama
Temsilci, en büyük sızıntının, yaklaşık 20 MB belleği kalıcı olarak tutan önceden ayrılmış hata nesneleriyle ilgili olduğunu tespit etti. Örneğin, tek bir statik hata sabiti (EMPTY_KEY_ERROR) tek başına 14,1 MB yer kaplıyordu.
Sorun: V8 gibi modern JavaScript motorlarında, Error nesnesi oluşturmak, hata ayıklamaya yardımcı olmak için mevcut çağrı yığınının anlık görüntüsünü alır. Tutulan boyutun bu kadar büyük olmasının nedeni tam olarak bu mekanizmadır:
- Hata, global bir sabitti: İlk modül değerlendirmesi sırasında örneklendirildi.
- JavaScript motoru yığını yakaladı: O sırada yığındaki etkin işlev, uygulamanın ana başlatma sarmalayıcısıydı (Anında Çağrılan İşlev İfadesi veya IIFE).
- Dizi, belleği hapsetti: Uygulama, hatayı hiçbir zaman çöp toplama işlemine tabi tutmadığı için dahili yığın izleme dizisi, ana paketin başlatma işlevine kalıcı bir referans tuttu.
// 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;
}
}
Çözüm: Ekip, kodu değiştirerek hataların ortaya çıktığında dinamik olarak oluşturulmasını sağladı.
Yeniden kullanılabilir statik bir hata kesinlikle gerekliyse yığını açıkça temizlemek (EMPTY_KEY_ERROR.stack = '';), yakalanan V8 dahili çerçevelerini hemen serbest bırakır.
// 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
Genel pencere etkinlik işleyicilerini ölçeklendirme
Temsilci, visibilitychange etkinlik işleyiciyle ilgili bir bellek ölçeklendirme sorunu tespit etti. Bu durum tam olarak bir "sızıntı" olmasa da (bileşen kaldırıldığında temizlendikleri için) ekrandaki her mesaj için bir dinleyici olduğundan bellek kullanımını artırıyordu. Anlık görüntüde 1.142 yerleşik etkinlik işleyici olduğu ve 128 etkin işleyicinin doğrudan global pencere nesnesine bağlı olduğu görüldü.
Sorun: JavaScript'te etkinlik işleyici geri çağırmaları, closure'lardır. Bir closure, ihtiyaç duyduğu değişkenlerin erişilebilir kalmasını sağlamak için kapsayıcı sözcüksel kapsamına yönelik referansları korur. Temsilci, bu dinleyicilerden 126'sını tek bir bileşene kadar takip etti: RelativeTimeController. Chat kullanıcı arayüzündeki her zaman damgası (ör. "5 dakika önce"), zamanı güncellemek için özel bir görünürlük dinleyicisi kaydeden kendi denetleyicisini yönetiyordu. Her dinleyici, denetleyici örneğini pencere köküne bağladığından, etkin kapanışlardan oluşan yoğun bir ağ, görünür sohbet ileti dizisinin kullanım ömrü boyunca önemli miktarda bellek kullanıyordu.
// 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
}
Çözüm: Ekip, global pencere etkinliklerini merkezileştirdi. Paylaşılan bir TimerService
artık pencerede tam olarak bir görünürlük dinleyicisi kaydeder ve güncellemeleri tüm etkin zaman damgası denetleyicilerine yayınlar. Bu sayede 126 dinleyici anında
tek bir dinleyiciye indirgendi.
// 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
}
}
Üçüncü taraf SDK'sı ve yerleştirilmiş iFrame tutucular
Ayrılmış DOM düğümleri, doküman ağacından kaldırılmış ancak hâlâ bir JavaScript değişkeni tarafından referans verilen öğelerdir. Ayrılmış bir DOM ağacındaki tek bir öğe bir kök değişken tarafından saklanıyorsa ayrılmış ağacın tamamı geri alınamaz.
Sorun: Kullanıcılar sohbet alanları arasında gezinirken temsilci, ayrılmış DOM ağaçlarının tamamının bellekte kaldığını fark etti. Bu referansın, Chat alanının kullanıcı arayüzündeki yerleşik bir medya oynatıcıya kadar uzandığı tespit edildi. Bir sohbet alanı kaldırıldığında kullanıcı arayüzü bileşeni sayfadan kaldırılıyordu ancak temel medya oynatıcı SDK'sı hiçbir zaman açıkça silinmiyordu. Üçüncü taraf kitaplığı, etkin iframe kimliklerinin dahili bir global sözlüğünü tutarak ayrılmış DOM'u canlı tutuyordu.
Çözüm: Tüm yerleştirilmiş iframe'lerin ve üçüncü taraf widget'ların bileşen imha yaşam döngülerine bağlı olduğundan emin olun. Oynatıcıda .destroy() yöntemini açıkça çağırmak, girişi global SDK kayıt defterinden temizler ve DOM'un çöp olarak toplanmasına olanak tanır.
// 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;
}
}
}
Ek kalıplar
Daha önce bahsedilen üç kalıp, tasarruf edilen ham megabayt açısından en etkili olanlar olsa da temsilci, 27'den fazla sızıntıyı daha tespit edip düzeltti. Bu senaryolarda şunlar amaçlanıyordu:
- Resim dizisi telemetrisi: Ayrılmış DOM ağaçlarının, ekranın üst kısmındaki resim metriklerini izleyen özel teşhis dizileri tarafından canlı tutulduğu durumlar
- Çerçeve bileşeni yaşam döngüsü hataları: Belge gövdesine yükseltilen özel açılır menülerin, orijinal üst öğelerinin bağlantısını kaldırmayı atladığı durumlar
- Örtük bağımsız değişkenler nesne kapanışı tuzağı: İç kapanışların, dış işlevlerin tüm bağımsız değişkenler dizisini istemeden yakaladığı durumlar
Etki: İş akışı olarak proaktif bellek yönetimi
Google Chat ekibi, kodlama ajanını ajanlar için Chrome Geliştirici Araçları ile donatarak son derece uzmanlık gerektiren bir görevi otomatikleştirdi. Karmaşık bir bellek sızıntısını izlemek, günlerce süren manuel yığın geçişinden dakikalar süren otomatik analize indi.
P2ER'nin kullanıcı arayüzü kararlılığını sağlamak için "Zorunlu Deneysel Doğrulama"yı uygulamasına benzer şekilde, ekipler bu bellek denetimi yaklaşımını proaktif bir önlem olarak benimseyebilir. Geliştiriciler, bellek sorunlarının yalnızca çok büyük veya karmaşık uygulamalarda yaşandığını varsayarak bu sorunları genellikle göz ardı eder. Ancak bu örnek olay incelemesinde gösterildiği gibi, standart etkinlik işleyiciler veya statik hatalar gibi yaygın kalıplar sessizce birikebilir. Ajanlar için Chrome Geliştirici Araçları gibi araçları günlük geliştirme döngünüze entegre etmek, yapay zekayı basit bir kod oluşturucudan, sessiz regresyonları üretime ulaşmadan önce yakalayan güvenilir bir kalite kontrol aracına dönüştürür.