Ana içeriğe geç

Bellek Sızıntısı: Nedenleri, Tespiti ve Nasıl Düzeltilir

Bellek sızıntılarına neyin sebep olduğunu, gerçek dünya örneklerini, tespit yöntemlerini ve performans sorunlarını önlemeye yönelik en iyi uygulamaları keşfedin.
Srujana Maddula
Srujana Maddula

Ücretsiz Hesabınızı Oluşturun

Alıştırma and program your progress.

Google ile devam edinDevam ederek, Kullanım Koşullarımızı ve Gizlilik Politikamızı kabul etmiş ve verilerinizin ABD'de saklandığını onaylamış olursunuz.

Çalıştırdığımız her program, büyük ya da küçük olsun, işlem yapmak için belleğe ihtiyaç duyar. Ancak bir program belleği kullanıp iş bittikten sonra serbest bırakmayı ihmal ederse, bu durum bellek sızıntısına yol açar. 

Zamanla daha fazla bellek sızıntısı, depolama yetersizliklerine ve yaklaşan görevlerin işlenememesine neden olabilir. Bu nedenle gereksiz hatalardan kaçınmak için bellek sızıntılarını tespit etmek ve yönetmek çok önemlidir.

Bu yazıda bellek sızıntılarını derinlemesine inceleyeceğiz. Hadi başlayalım!

Bellek Sızıntısı Nedir?

Bellek sızıntıları, bir program veya uygulama belleği kullanıp iş bittikten sonra serbest bırakmadığında ortaya çıkar. Bellekten kastım RAM’dir—bunu sabit disk belleğiyle karıştırmayın.  Bu sızıntılar yavaş yavaş birikir ve RAM’i yeni işlemleri kaldıramayacak kadar doldurur. 

Bellek sızıntıları RAM’i tüketir ve G/Ç işlemlerini artırarak performansı etkiler. Bellek sızıntıları biriktikçe sistem, verileri diske aktararak RAM’i boşaltmaya çalışır; bu da disk G/Ç işlemlerinde artışa yol açar.

Bellek sızıntıları hassas verileri engellediğinde güvenlik de riske girer. Parola veya şifreleme anahtarları gibi bilgiler RAM’de gerekenden uzun süre kalırsa, saldırganlara karşı daha savunmasız hale gelir.

Popüler Programlama Dillerinde Bellek Sızıntısı Örnekleri

Bellek sızıntılarının nasıl oluştuğunu daha iyi anlamak için, bunları bazı örneklerle iş başında görmekten daha iyisi yoktur. 

Python’da bellek sızıntıları

Python, bellek yönetimi için başvuru sayımına dayanır. Bir nesneye atıfta bulunan diğer nesnelerin sayısı olan başvuru sayısı sıfıra düştüğünde nesneyi kaldırır. Ancak, döngüsel başvuruları ele almamak gibi bazı sınırlamaları vardır.

Terime aşina olmayanlar için, döngüsel başvuru iki veya daha fazla değişkenin birbirine dairesel şekilde atıfta bulunmasıyla oluşur. 

Örneğin, a nesnesi b’ye, b c’ye ve c yeniden a’ya atıfta bulunuyorsa, bu sonsuz bir döngüdür. Burada başvuru sayısı asla 0 olamaz ve nesneler bellekte sonsuza dek kalır; bu da Python’da bellek sızıntılarına yol açar.

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None 

# Create two nodes
node1 = Node(1)
node2 = Node(2)

# Establish a circular reference
node1.next = node2  # node1 refers to node2
node2.next = node1  # node2 refers back to node1

Yukarıdaki kod parçacığı bir döngüsel başvuru gösterir. Python’un bir çöp toplayıcısı vardır ve bu duramları ele alır. Yine de, özellikle global değişkenler kullanıldığında bazı senaryoları ele almakta başarısız olursa, artık kullanılmayan nesne başvurularını None olarak ayarlayarak bellek sızıntılarını manuel olarak yönetmelisiniz. 

Çalışma süresini hızlandırırken daha az kaynak tüketmek için Python’da bellek açısından verimli sınıflar yazmayı öğrenmenizi öneririm.

Java’da bellek sızıntıları

Java belleği otomatik olarak yönetir ve programcılardan açıkça yardım gerektirmez. Ancak, hatalı dinleyici (listener) kayıtları veya statik başvurular gibi durumlarda, çöp toplayıcı alanı serbest bırakamayabilir ve bu da bellek sızıntılarına neden olur. Bu nasıl olabilir görelim:

  • Bir olay kaynağı olaylar üretir ve bir dinleyici nesnesi bu olaylar gerçekleştiğinde bilgilendirilmek üzere kendini kaydeder. 
  • Olay kaynağı dinleyiciye güçlü bir başvuru tutarsa, dinleyici artık kullanılmıyor olsa bile, olay kaynağı canlı kaldığı sürece dinleyici çöp toplanamaz. 

Bu gibi durumlarda bellek sızıntıları ortaya çıkabilir. Bunlardan kaçınmak için, dinleyicileri artık ihtiyaç kalmadığında manuel olarak kayıttan çıkarın.

Bellek sızıntılarının bir diğer yaygın nedeni statik değişkenlerdir. Statik değişkenler tüm yaşam döngüsü boyunca bellekte tutulur. Bu nedenle, nesnelere atıfta bulunuyorlarsa, bu nesneler artık kullanılmıyor olsalar bile çöp toplanmaz ve bellek sızıntısına neden olur. Örneğin:

import java.util.ArrayList;
import java.util.List;

public class StaticMemoryLeak {

    // Static list
    private static List<Object> staticList = new ArrayList<>();

    public static void addObject(Object obj) {
        // Add the object to the static list
        staticList.add(obj);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            Object obj = new byte[1024 * 1024];
            // Add the object to the static list
            addObject(obj);
        }
    }
}

Yukarıdaki programda, staticList değişkeni statik bir dizi olarak bildirilmiştir ve içine nesneler eklenir. Döngü 1000 kez çalışır; her seferinde 1 MB nesne eklenir ve toplamda 1 GB bellek oluşur. 

Liste statik olduğu için, program yaşadığı sürece nesnelere olan başvuruları asla kaybetmez. Dolayısıyla çöp toplayıcı bu nesneleri silemez ve 1 GB bellek tüm yaşam döngüsü boyunca tutulur. Bu kullanılmayan nesne belleği OutOfMemoryError hatalarına neden olabilir. Bunu düzeltmek için açıkça staticList.clear() çağrısı yapıp belleği serbest bırakmalısınız.

C/C++’ta bellek sızıntıları

Java ve Python’un aksine, C++ otomatik çöp toplayıcılar içermez. Dolayısıyla C++’ta bellek sızıntıları, programcılar belleği ayırıp serbest bırakmayı unuttuğunda meydana gelir. Örneğin, 

void sample_leak() { 
    int* ptr = new int(5);
    return; 
}

Yukarıdaki parçacıkta, işaretçi 5 tamsayısının depolandığı bellek adresini tutar. Fonksiyon bittiğinde yerel değişken ptr kapsam dışında kalır, ancak yığında ayrılan bellek kalır ve bellek sızıntısına neden olur. 

Bunu önlemek için belleği serbest bırakmak üzere açıkça delete ptr; satırını ekleyin.

JavaScript’te bellek sızıntıları

JavaScript, belleği otomatik olarak yöneten bir çöp toplayıcıya sahiptir. Şu adımlarla çalışır: kökü bulur, yolunu özyinelemeli olarak çocuklarına kadar takip eder ve her çocuğu etkin ya da etkin değil olarak işaretler. Son olarak etkin olmayan tüm başvuruları siler. 

Ancak aşağıdaki durumlarda çöp toplayıcı belleği serbest bırakmayı başaramayabilir:

  1. Global değişkenler: Bir değişkeni let, var veya const ile bildirmeyi unutursanız, JavaScript onu otomatik olarak global bir değişken olarak oluşturur. Program çalıştığı sürece global değişkenler bellekte kalır ve daima erişilebilirdir; bu da çöp toplayıcının onları serbest bırakmasını engeller. 
  2. setTimeOut(): setTimeOut(), belirli bir sürenin ardından yürütülecek bir geri çağırma (callback) işlevi planlar. Bu geri çağırma işlevi bir başvuruyu daha uzun süre elinde tuttuğunda bellek sızıntısı oluşabilir. İşte bir örnek:
function TimeoutExample() {
  var obj = 10; 
  setTimeout(function() {
    console.log(obj); 
  }, 1000);  // This runs after 1 second
}

TimeoutExample();

Yukarıdaki kodda, TimeoutExample() işlevi bitmiş olsa bile, obj geri çağırma işlevi yürütülene kadar bir başvuruyu tutmaya devam eder; çünkü obj geri çağırma içinde kullanılmaktadır. Bu büyük nesnelerle gerçekleşirse, ciddi bellek sızıntılarına yol açar. 

Bellek Sızıntılarının Nedenleri

Belirtildiği gibi, bellek sızıntıları çeşitli programlama dillerinde farklı nedenlerle ortaya çıkabilir. Bazı diller otomatik çöp toplamaya sahip değildir; diğerleri ise anormal senaryolarda belleği serbest bırakmayı başaramayabilir. 

Şimdi bazı yaygın nedenlere ayrıntılı bakalım:

  • Serbest bırakılmayan başvurular: Nesne veya değişken oluşturduğumuzda, sistem onlar için bellek veya başvurular ayırır. Kullanımdan sonra bu başvuruları kapatmayı ihmal etmek belleği bloke edebilir. Bu tür bellek zamanla birikir ve bellek sorunlarına yol açar. 
  • Döngüsel başvurular: Döngüsel başvurular, birden fazla nesnenin birbirine atıfta bulunarak bir döngü oluşturmasıyla meydana gelir. Nesneler artık kullanılmıyor olsa bile, döngüdeki diğer nesneler tarafından hâlâ başvuruldukları için çöp toplayıcı onların belleğini geri kazanamaz.
  • Hatalı kaynak yönetimi: Bazen veritabanı bağlantılarını, ağ soketlerini veya dosya tanıtıcılarını kullanım sonrası düzgün şekilde kapatmak için ekstra çaba göstermeyiz. Bu ihmal ciddi bellek sızıntılarına yol açabilir. Örneğin, İS açık dosyaları işlemek için sınırlı dosya tanıtıcılarına sahiptir. Dosyalar asla kapatılmazsa, İS dosya tanıtıcılarını tüketir ve sistemin yeni dosyalar açmasını engeller.
  • Statik değişkenlerin hatalı kullanımı: Bellek sızıntıları sıklıkla statik değişkenlerin aşırı kullanımından kaynaklanır. Statik değişkenler programın tüm ömrü boyunca canlı kaldığından, atıfta bulundukları nesneler—kullanılmıyor olsalar bile—bellekte kalır. Bellek sızıntılarını önlemek için bu nesneler açıkça serbest bırakılmalı ya da statik değişken kullanımı kaçınılmalıdır.
  • Harici kütüphane ve çerçeveler: Üçüncü taraf kütüphaneler ve harici çerçeveler, verimsiz kaynak yönetimi nedeniyle bellek sızıntılarına yol açabilir. Sistem kaynaklarınızı kullanıp serbest bırakmayı başaramazlarsa bellek sızıntıları oluşur. 

Bellek Sızıntıları Nasıl Tespit Edilir?

Artık bellek sızıntılarının neden ortaya çıkabileceğini incelediğimize göre, bunları fark etmek ve hata ayıklamak için bazı güvenilir tespit yöntemlerini tartışacağız.

  • Manuel inceleme: Dikkatlice kodu gözden geçirerek döngüsel başvurular, statik değişkenler, eksik serbest bırakma ifadeleri veya kullanılmayan nesnelere başvuran değişkenler gibi olası bellek sızıntısı nedenlerini belirleyin.  Koddaki bu kalıpları titizlikle inceleyerek sorunu tam olarak tespit edebilir ve proaktif şekilde ele alabilirsiniz. 
  • Hata ayıklama araçlarının kullanımı: Bellek sızıntılarını tespit etmek için farklı programlama dillerine özgü çeşitli hata ayıklama araçları ve kütüphaneler vardır. Örneğin, Python’un tracemalloc modülü, farklı zaman noktalarındaki bellek anlık görüntülerini karşılaştırmamıza ve bu zaman aralıklarındaki bellek tahsislerini izlememize olanak tanır.  Ayrıca, Python’un memory profiler’ı her bir kod satırını debug etmeye ve beklenenden fazla bellek kullanımına sahip alanları belirlemeye imkan tanır.
  • Bellek kullanımını izleme: Bellek izleme araçları, bellek tüketimini proaktif biçimde takip eder ve sorunları gerçek zamanlı olarak tespit eder. Bu araçlar kritik bellek sorunları konusunda sizi uyarır, ilgili günlükleri sağlar ve bunları nasıl düzelteceğinize dair öneriler sunar. Örnekler arasında Valgrind, Paessler, AddressSanitizer ve diğerleri yer alır. 
  • Sızıntılar için test yazma: Bellek sızıntılarını tespit eden birim testleri eklemeyi düşünün. Böylece, işlevsellik ve performans sorunlarına ek olarak, bellek sızıntıları da geliştirme sırasında saptanır. 
  • Entegrasyon testleri yazma: Entegrasyon testleri de gerçek dünya senaryolarını simüle etmek ve üretimde ortaya çıkabilecek potansiyel bellek sızıntılarını tespit etmek için kullanılmalıdır.

Bellek Sızıntılarını Önlemek için En İyi Uygulamalar

Bellek kaynaklarına dayanan uygulamalarda bellek sızıntıları yaygındır. Bunları önlemek için aşağıdaki birkaç uygulamayı izleyebilirsiniz. 

Verimli kaynak yönetimi

Garbage collector kullanarak belleği otomatik olarak yöneten Java veya Python gibi dilleri kullanmaya çalışın. Aksi takdirde, C veya C++ gibi dillerde ayrılan tüm belleğin açıkça silindiğinden emin olun. 

Ayrıca, verimli kaynak temizliği için Python’da with gibi bağlam yöneticilerini veya Java’da try-with-resources ya da finally bloklarını kullanın. Bu uygulamalar bellek sızıntılarını önlemeye ve uygun kaynak yönetimini sağlamaya yardımcı olur. 

Zayıf başvurular kullanın

Güçlü başvuruların aksine, zayıf başvurular nesnelerin çöp toplanmasını engellemez. Nesnelere başvuru tutsalar bile nesneye erişilebilirlik hesabına katılmazlar. Dolayısıyla çöp toplayıcı, o nesneyle ilişkili belleği geri kazanabilir. Bu nedenle güçlü başvurular yerine zayıf başvurular kullanın. 

Sık kod incelemeleri

Bellek sızıntılarını belirlemek için düzenli kod incelemeleri yapmak önemlidir. Kodu gözden geçirirken kapatılmamış kaynaklar, döngüsel başvurular, global veya statik değişkenler ve verimsiz önbellekleme gibi yaygın kalıplara bakın. Böylece sorun, sisteminizi etkilemeden önce hızla bulunur. 

Sonuç

Bu yazıda, bellek sızıntılarının nedenlerinden ve örneklerinden tespit tekniklerine kadar temel yönlerini ele aldık. Farklı senaryoların bellek sızıntılarına nasıl yol açabileceğini ve bunları nasıl ele alabileceğinizi gördük. Ayrıca, gelecekte bellek sızıntılarını önlemek için izlemeniz gereken en iyi uygulamaları da tartıştık.  

Farklı programlama dillerinde bellek sızıntılarını incelediğimize göre, anlayışınızı daha da derinleştirmek için aşağıdaki kursları keşfetmenizi öneririm:

SSS

Bellek sızıntısının yaygın belirtileri nelerdir?

Bellek sızıntılarının sisteminizi etkilediğini gösteren bazı yaygın belirtiler: sistemde yavaşlama, tarayıcının yavaşlaması, uygulama veya işletim sistemi çökmeleri. 

Hangi diller bellek sızıntılarına daha yatkındır?

C ve C++ gibi diller, belleği otomatik olarak yöneten çöp toplayıcılar olmadığı için bellek sızıntılarına daha yatkındır. Bunun yerine belleği manuel olarak ayırıp serbest bırakmalısınız.

Modern programlama dillerinde bellek sızıntıları oluşabilir mi?

Evet! Modern programlama dillerindeki otomatik çöp toplama, hatalı olay dinleyici kayıtları ve statik veya global değişkenler gibi senaryoları her zaman ele alamaz.

Bellek sızıntısını nasıl düzeltirim?

Bir bellek sızıntısını düzeltmek için şu adımları izleyin:

  • Sızıntıyı tespit edin: profil oluşturma araçlarını kullanın.
  • Nedeni analiz edin: kapatılmamış dosya tanıtıcıları veya kalıcı başvurular gibi.
  • Belleği serbest bırakın: C/C++ gibi dillerde açıkça, çöp toplanan dillerde ise başvuruları doğru yönetin.
  • Bellek yönetimini optimize edin: zayıf başvurular ve uygun nesne yaşam döngüsü yönetimi gibi en iyi uygulamaları kullanın.
Srujana Maddula

Yazar

Srujana Maddula

Srujana, Bilgisayar Bilimleri alanında dört yıllık dereceye sahip serbest çalışan bir teknoloji yazarıdır. Veri bilimi, bulut bilişim, geliştirme, programlama, güvenlik ve daha birçok konuyu yazmak onun için doğal bir süreçtir. Klasik edebiyata ve yeni yerler keşfetmeye tutkuyla bağlıdır.

Konular

İlgili