MERHABA!

Bilgisayarınızın sınırlı miktarda belleği olduğunu söylesem pek şaşırmayacağınızı düşünüyorum :) Bir sabit sürücü bile - genellikle RAM depolama alanından kat kat daha büyük - en sevdiğiniz oyunlar, TV şovları, ve dahası. Bunun olmasını önlemek için, belleğin mevcut durumunu izlemeniz ve gereksiz dosyaları bilgisayarınızdan silmeniz gerekir. Java programlamanın tüm bunlarla ne ilgisi var? Her şey! Sonuçta, Java makinesi herhangi bir nesne oluşturduğunda, o nesne için bellek ayırır.

Gerçek bir büyük programda, onlarca ve yüzbinlerce nesne oluşturulur ve her birinin kendisine ayrılmış kendi hafızası vardır. Ama tüm bu nesnelerin ne kadar süredir var olduğunu düşünüyorsun? Programımızın çalıştığı süre boyunca "yaşıyorlar" mı? Tabii ki değil. Java nesnelerinin tüm avantajlarına rağmen ölümsüz değiller :) Nesnelerin kendi yaşam döngüleri vardır. Bugün kod yazmaya biraz ara verip bu sürece bakacağız :) Üstelik bir programın nasıl çalıştığını ve kaynakların nasıl yönetildiğini anlamanız için çok önemli. Peki bir cismin ömrü ne zaman başlar? Bir insan gibi - doğumundan, yani yaratılıştan.


Cat cat = new Cat(); // Here the lifecycle of our Cat object begins!

İlk olarak, Java Sanal Makinesi, nesneyi oluşturmak için gerekli miktarda bellek ayırır. Sonra o hafızaya bir referans oluşturur. Bizim durumumuzda, bu referans çağrılır cat, böylece onu takip edebiliriz. Sonra tüm değişkenleri başlatılır, yapıcı çağrılır ve — ta-da! — yeni basılan nesnemiz kendi hayatını yaşıyor :)

Nesnelerin ömürleri değişkenlik gösterdiğinden burada kesin rakamlar veremiyoruz. Her durumda, bir süre programın içinde yaşar ve işlevlerini yerine getirir. Kesin olmak gerekirse, bir nesne, ona referanslar olduğu sürece "canlıdır". Referans kalmadığı anda nesne "ölür". Örnek:


public class Car {
  
   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");
       lamborghini = null;

   }

}

Lamborghini Diablo araba nesnesi, yöntemin ikinci satırında canlı olmayı durdurur main(). Buna yalnızca bir referans vardı ve sonra bu referans, olarak ayarlandı null. Lamborghini Diablo'ya referans kalmadığından, ayrılan hafıza "çöp" olur. Bunun gerçekleşmesi için bir referansın null değerine ayarlanması gerekmez:


public class Car {

   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");

       Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
       lamborghini = lamborghiniGallardo;
   }

}

Burada ikinci bir nesne oluşturduk ve ardından bu yeni nesneyi referansa atadık lamborghini. Şimdi Lamborghini Gallardonesnenin iki referansı var, ancak Lamborghini Diablonesnenin hiçbiri yok. Bu, nesnenin artık çöp olduğu anlamına gelir Diablo. Bu, Java'nın çöp toplayıcı (GC) adı verilen yerleşik mekanizmasının devreye girdiği zamandır.

Çöp toplayıcı, belleği boşaltmaktan, yani gereksiz nesneleri bellekten kaldırmaktan sorumlu dahili bir Java mekanizmasıdır. Burada bir robot elektrikli süpürge resmi seçmemizin iyi bir nedeni var. Ne de olsa, çöp toplayıcı hemen hemen aynı şekilde çalışır: arka planda, programınız hakkında "seyahat eder" ve neredeyse hiç çaba harcamadan çöpleri toplar. Görevi, programda artık kullanılmayan nesneleri kaldırmaktır.

Bunu yapmak, bilgisayardaki diğer nesneler için bellekte yer açar. Dersin başında sıradan hayatta bilgisayarınızın durumunu izlemeniz ve gereksiz dosyaları silmeniz gerektiğini söylediğimizi hatırlıyor musunuz? Java nesneleri söz konusu olduğunda, çöp toplayıcı bunu sizin için yapar. Çöp toplayıcı, programınız çalışırken art arda çalışır: teknik olarak mümkün olmasına rağmen, onu açıkça çağırmanız veya komutlar vermeniz gerekmez. Daha sonra bunun hakkında daha fazla konuşacağız ve çalışmalarını daha ayrıntılı olarak analiz edeceğiz.

finalize()Çöp toplayıcı bir nesneye ulaştığında, nesneyi yok etmeden hemen önce, nesne üzerinde özel bir yöntemi — — çağırır . Bu yöntem, nesne tarafından kullanılan diğer kaynakları serbest bırakabilir. Yöntem finalize(), sınıfın bir parçasıdır Object. equals()Bu , daha önce tanıştığınız hashCode()ve yöntemlerine ek olarak, her nesnenin bu yönteme sahip olduğu anlamına gelir . toString()Diğer yöntemlerden farkı - bunu nasıl söylesem - çok kaprisli olmasıdır.

Özellikle, bir nesnenin yok edilmesinden önce her zaman çağrılmaz. Programlama kesin bir çabadır. Programcı bilgisayara bir şey yapmasını söyler ve bilgisayar onu yapar. Sanırım bu davranışa zaten alışkınsınız, bu nedenle ilk başta şu fikri kabul etmeniz zor olabilir: "Nesnelerin yok edilmesinden önce, sınıfın yöntemi çağrılır. finalize()Veya Objectbelki de çağrılmaz. Her şey bağlıdır. Senin şansın!"

Yine de bu doğru. Java makinesi, yöntemin çağrılıp çağrılmayacağına finalize()duruma göre karar verir. Örneğin, aşağıdaki kodu bir deneme olarak çalıştırmayı deneyelim:


public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public Cat() {
   }

   public static void main(String[] args) throws Throwable {
       for (int i = 0 ; i < 1000000; i++) {
           Cat cat = new Cat();
           cat = null; // This is when the first object becomes available to the garbage collector
       }
   }

   @Override
   protected void finalize() throws Throwable {
       System.out.println("Cat object destroyed!");
   }
}

Bir nesne yaratıyoruz Catve sonraki kod satırında onun tek referansını null'a eşitliyoruz. Ve bunu milyonlarca kez yapıyoruz. Yöntemi açıkça geçersiz kıldık finalize(), böylece konsola bir milyon kez bir dize yazdırır (bir Catnesneyi her yok edişinde bir kez). Ama hayır! Kesin olmak gerekirse, bilgisayarımda yalnızca 37.346 kez çalıştı! Yani, 27 seferde sadece bir kez makinemde yüklü olan Java makinesi finalize()yöntemi çağırmaya karar verdi.

Diğer durumlarda, çöp toplama onsuz gerçekleşti. Bu kodu kendiniz çalıştırmayı deneyin: büyük olasılıkla farklı bir sonuç alacaksınız. Gördüğünüz gibi, finalize()pek güvenilir bir ortak olarak adlandırılamaz :) Bu nedenle, gelecek için küçük bir tavsiye: Kritik kaynakları serbest bırakma yöntemine güvenmeyin finalize(). Belki JVM onu arayacak ya da aramayacak. Kim bilir?

Nesneniz hayattayken performans için çok önemli olan bazı kaynakları içeriyorsa, örneğin açık bir veritabanı bağlantısı, sınıfınızda bunları serbest bırakmak için özel bir yöntem oluşturmak ve ardından nesne artık olmadığında onu açıkça çağırmak daha iyidir. gerekli. Bu şekilde, programınızın performansının zarar görmeyeceğinden emin olacaksınız. En başından beri bellekle çalışmanın ve çöpleri kaldırmanın çok önemli olduğunu söyledik ve bu doğru. Kaynakların yanlış kullanılması ve gereksiz nesnelerin nasıl temizlendiğinin yanlış anlaşılması bellek sızıntılarına yol açabilir. Bu en iyi bilinen programlama hatalarından biridir.

Programcılar kodlarını yanlış yazarsa, her seferinde yeni oluşturulan nesneler için yeni bellek ayrılabilirken, eski, gereksiz nesneler çöp toplayıcı tarafından kaldırılamayabilir. Bir robot elektrik süpürgesi ile benzetme yaptığımıza göre, robotu çalıştırmadan önce evin etrafına çorap saçarsanız, bir cam vazoyu kırarsanız ve yere bir Lego yapı taşı bırakırsanız ne olacağını hayal edin. Robot işini yapmaya çalışacak elbette ama bir noktada takılıp kalacak.

Robot elektrikli süpürgenin düzgün çalışması için zemini iyi durumda tutmanız ve robotun kaldıramayacağı her şeyi kaldırmanız gerekir. Aynı ilke Java'nın çöp toplayıcısı için de geçerlidir. Bir programda temizlenemeyecek çok sayıda nesne kaldıysa (bir çorap veya robot elektrikli süpürgemiz için Lego yapı taşı gibi), bir noktada hafızanız tükenir. Ve donacak olan sadece sizin programınız olmayabilir; bilgisayarda çalışan diğer tüm programlar etkilenebilir. Onların da yeterli hafızası olmayabilir.

Bunu ezberlemenize gerek yok. Sadece nasıl çalıştığının arkasındaki prensibi anlamanız gerekiyor.