Uygulamayı yükle
How to install the app on iOS

Follow along with the video below to see how to install our site as a web app on your home screen.

Not: This feature may not be available in some browsers.

Closure Kullanımında Yaşanan Bellek Sızıntıları ve Memory Management Sorunları

batuhanunalir

Aktif üye
Yazar
Katılım
27 Tem 2025
Konular
567
Mesajlar
576
Tepkime puanı
144
Puanları
43
🌟Puan
226
💵Bakiye
0TL

Closure Kullanımında Yaşanan Bellek Sızıntıları ve Memory Management Sorunları​

JavaScript dünyasının en güçlü ve karakteristik özelliklerinden biri olan Closure (Kapatma/Kapsama), bir fonksiyonun, kendi dışındaki kapsamda (lexical scope) bulunan değişkenleri, o dış kapsamın infazı bittiğinde dahi hatırlayabilmesi ve bu değişkenlere erişebilmesi yeteneğidir. Veri gizleme, modüler kod tasarımı ve fonksiyonel programlama pratiklerinde benzersiz avantajlar sunan bu mekanizma, arka planda ciddi bir bellek yönetim (memory management) maliyeti barındırır. Geliştiriciler tarafından dikkatsizce veya bilinçsizce kurgulanan closure yapıları, JavaScript motorunun çöp toplama (garbage collection) algoritmasını yanıltarak bellek sızıntılarına (memory leaks) neden olur. Bu yazıda, closure yapılarının bellek üzerindeki yaşam döngüsünü, çöp toplama motorunun işleyişini ve kurumsal projeleri felç edebilen bellek sızıntısı senaryolarını inceleyeceğiz.

Lexical Scope ve Closure Yapılarının Bellek Yaşam Döngüsü​

JavaScript'te her fonksiyon çalıştırıldığında, o fonksiyona ait değişkenlerin, parametrelerin ve iç fonksiyon tanımlamalarının saklandığı bir Execution Context (Yürütme Bağlamı) ve buna bağlı bir Lexical Environment (Sözdizimsel Ortam) oluşturulur. Normal şartlar altında, senkron çalışan bir fonksiyon görevini tamamlayıp Call Stack'ten (Çağrı Yığınından) atıldığında, ona ait olan bu sözdizimsel ortam da bellekten tamamen silinir.

Ancak, bir fonksiyon kendi içinde başka bir fonksiyon tanımlayıp onu dış dünyaya return ettiğinde veya asenkron bir callback yapısına geçirdiğinde kurallar tamamen değişir. İçteki fonksiyon, dıştaki fonksiyonun değişkenlerine bağımlı olduğu sürece, dış fonksiyonun yürütmesi bitse bile onun sözdizimsel ortamı bellekten temizlenemez. İç fonksiyon, dış kapsamı bir görünmez bağ ile kendine kenetler. Bellekte (Heap) canlı tutulan bu referans zincirine closure denir. Closure var olduğu sürece, o kapsama ait tüm değişkenler bellekte yer kaplamaya devam eder.

JavaScript Çöp Toplama (Garbage Collection) Mekanizması Nasıl Çalışır?​

Modern JavaScript motorları, belleği otomatik olarak yönetmek için Mark-and-Sweep (İşaretle ve Süpür) adlı bir çöp toplama algoritması kullanır. Bu algoritma, bellekteki her bir verinin hala ihtiyaç duyulup duyulmadığını tespit etmek için kök nesneden (çoğunlukla tarayıcıdaki window veya Node.js'teki global) başlar ve tüm referans bağlarını bir ağaç gibi tarar.

Algoritma, kök nesneden başlayarak ulaşılan (reachable) tüm nesneleri "canlı" olarak işaretler. Tarama bittiğinde, kök nesneden uzanan referans zinciriyle hiçbir şekilde ulaşılamayan (unreachable) tüm bellek blokları "çöp" olarak kabul edilir ve sistem tarafından süpürülerek belleğe geri kazandırılır. Closure mekanizmasında yaşanan temel problem, artık kullanılmayan veya ihtiyaç duyulmayan bazı değişkenlerin, canlı bir iç fonksiyon referansı nedeniyle hala "ulaşılabilir" görünmesi ve bu yüzden çöp toplama motoru tarafından asla silinememesidir. Geliştiricinin farkında olmadığı bu görünmez ulaşılabileme durumuna bellek sızıntısı denir.

Closure Kaynaklı Sık Yaşanan Bellek Sızıntısı Senaryoları​

Closure yapılarının sebep olduğu bellek sızıntıları genellikle sessizce gerçekleşir. Kod hata fırlatmaz, uygulama düzgün çalışıyor gibi görünür; ancak sistem arka planda her döngüde daha fazla bellek tüketerek zamanla yavaşlar veya tamamen çöker.

Temizlenmeyen Event Listener (Olay Dinleyicisi) Bağlantıları​

Web sayfalarında DOM elementlerine eklenen olay dinleyicileri, kendi dışlarındaki büyük veri yapılarını closure yardımıyla referans aldıklarında büyük risk oluştururlar. Örneğin, büyük bir veri tablosunu işleyen bir fonksiyonun içinde, sayfadaki bir butona tıklama olayı eklendiğini ve bu olay fonksiyonunun (callback) tablodaki verilere eriştiğini düşünelim.

Kullanıcı sayfada başka bir bölüme geçtiğinde veya o buton DOM'dan silindiğinde, eğer eklenen olay dinleyicisi tarayıcı motorundan manuel olarak kaldırılmazsa, arka plandaki o devasa veri tablosu bellekte tutulmaya devam eder. Çünkü olay dinleyicisi hala aktiftir ve içindeki closure yapısı nedeniyle o büyük veriyi kök nesneye bağlı, yani "ulaşılabilir" tutmaktadır.

Eşzamanlı Kapsam Paylaşımı (Shared Scope) Tehlikesi​

JavaScript motorları, aynı dış fonksiyon içinde tanımlanan tüm iç fonksiyonlar için tek bir ortak sözdizimsel ortam (shared lexical environment) oluşturur ve optimize eder. Bu optimizasyon, bazen çok büyük ve tehlikeli bir bellek sızıntısı tuzağına dönüşebilir.

Bir fonksiyon içinde iki adet iç fonksiyon tanımlandığını varsayalım. Bu fonksiyonlardan bir tanesi çok büyük bir veri bloğunu (örneğin megabaytlarca büyüklükte bir string veya dizi) closure olarak saklasın, diğer iç fonksiyon ise sadece küçük bir değişkeni kullansın. Eğer biz büyük veriyi kullanan fonksiyonu hiçbir yerde çalıştırmayıp tamamen unutsak bile, sadece küçük fonksiyonu dış dünyaya aktarıp canlı tuttuğumuzda, her iki fonksiyon aynı ortak ortamı paylaştığı için o devasa veri bloğu da bellekte kilitli kalır. JavaScript motoru ortamı bütünsel olarak koruduğu için, kullanılmayan büyük veri çöp toplama motoru tarafından süpürülemez.

Global Değişkenlere Bağlanan Kronik Döngüler​

Sürekli arka planda çalışan ve zamanlayıcılar (setInterval veya setTimeout) tarafından tetiklenen fonksiyonlar, closure yapılarını küresel ölçekte canlandırabilir. Zamanlayıcı fonksiyonu her çalıştığında dış kapsamdaki değişkenleri manipüle ediyorsa ve bu süreç durdurulmuyorsa, bellek tüketimi doğrusal olarak artar. Zamanlayıcının kendisi küresel kapsama bağlı olduğu için, onun closure ile tuttuğu her alt nesne de sistem açık kaldığı sürece bellekten asla atılamaz.

Bellek Yönetimi Optimizasyonu ve Çözüm Stratejileri​

Closure yapılarının getirdiği güçten vazgeçmeden, uygulamanın bellek sağlığını korumak bütünüyle doğru kod mimarisi kurmaktan geçer.

  • Referansları Manuel Olarak Sıfırlayın: Eğer bir closure yapısının görevi bittiyse ve içindeki büyük veriye artık ihtiyaç yoksa, dış dünyadaki fonksiyon referansını veya ilgili değişkenin değerini açıkça boş (null) hale getirin. Bu işlem, referans zincirini kopararak Mark-and-Sweep algoritmasının o alanı temizlemesini sağlar.
  • Yaşam Döngülerini Yönetin: Eklenen her olay dinleyicisini, işi bittiğinde veya bağlı olduğu bileşen ekrandan kaldırıldığında mutlaka sistemden temizleyin.
  • Zayıf Referansları Kullanın (WeakMap ve WeakSet): Nesneleri closure içinde sert bağlarla tutmak yerine, JavaScript'in yerleşik zayıf referans mimarilerini tercih edin. Bu özel yapılar, içindeki nesnelerin çöp toplayıcı tarafından silinmesine engel olmaz. Eğer nesneye dışarıdan başka hiçbir sert referans kalmadıysa, sistem o nesneyi closure içinde olsa dahi bellekten temizleyebilir.

Sonuç ve Mimari Değerlendirme​

Closure, JavaScript programlama dilinin en asil yapılarından biridir ve doğru kullanıldığında esnek, güvenli ve modüler sistemler inşa etmeyi sağlar. Ancak, bellek yönetimi perspektifinden bakıldığında, her closure yapısının bellekte görünmez bir maliyet hesabı açtığı unutulmamalıdır.

Büyük ölçekli projelerde bellek sızıntılarını önlemek için kod yazarken şu farkındalık korunmalıdır: Bir fonksiyonun ömrünü uzattığınızda, o fonksiyonun dokunduğu ve kapsadığı tüm veri dünyasının da ömrünü uzatmış olursunuz. Değişkenlerin ve fonksiyonların yaşam döngülerini sınırlandırmak, kullanılmayan referansları zamanında koparmak, uygulamanızın bellek performansını her zaman en üst seviyede tutacaktır.
 
Harika bir teknik analiz paylaşmışsınız, elinize sağlık! Özellikle "Shared Scope" (Eşzamanlı Kapsam Paylaşımı) konusuna değinmeniz çok kıymetli; zira birçok geliştirici, kullanmadığı bir değişkenin sırf aynı ortamdaki başka bir fonksiyon nedeniyle bellekte tutulduğunu fark etmeyebiliyor.

Bu derinlikli yazıya küçük bir ekleme yapmak gerekirse; modern framework'lerin (React, Vue vb.) "Effect" yapılarında (örneğin useEffect içindeki return fonksiyonu) bu temizlik işlemlerinin unutulması, günümüzde en sık rastlanan "modern closure sızıntısı" kaynağı haline geldi.

Özellikle WeakMap kullanımı, belirttiğiniz gibi büyük veri setlerini yönetirken hayat kurtarıcı bir çözüm. Konu hakkında deneyimi olan diğer arkadaşların, özellikle Chrome DevTools'un "Memory" sekmesinde Heap Snapshot alırken yaşadıkları ilginç sızıntı hikayeleri varsa dinlemek isteriz.

Paylaşım için teşekkürler!
 

Konuyu izleyenler

Benzer konular

Günün trendleri

Geri