Java'da bellek yönetimi, otomatik çöp toplama (Garbage Collector, GC) aracılığıyla gerçekleştirilir. JVM, bellek içerisindeki nesneleri kendisi izler: eğer bir nesneye artık referans yoksa, erişilemez hale gelir ve silinebilir.
GC, Mark, Sweep ve bazen Compact gibi farklı aşamalardan oluşur. Mark aşamasında, köklerden (GC roots) erişilebilen nesneler canlı olarak işaretlenir. Ardından Sweep başlar: kullanılmayan nesneler silinir. Bazen Compact uygulanır — bellek parçalarının yeniden düzenlenmesi.
Farklı GC türleri vardır: Serial, Parallel, CMS, G1. Uygulamanın yük türüne bağlı olarak bunları seçmek önemlidir.
List<byte[]> dataList = new ArrayList<>(); while(true) { dataList.add(new byte[1024*1024]); // Nesneler erişilebilir kalır }
Bu örnekte nesneler asla erişilemez hale gelmeyecek, GC onları silmeyecek ve OutOfMemoryError meydana gelecektir.
Bir nesneye daha fazla referans yoksa, onun finalize() yönteminin çağrılacağı garanti ediliyor mu?
Cevap: finalize() yönteminin çağrılması asla garanti edilmez! Hiç çağrılmayabilir ya da gecikmeli olarak çağrılabilir. Kaynakları serbest bırakmak için finalize()'a güvenilmemelidir.
@Override protected void finalize() throws Throwable { // Çalışmayabilir! }
Hikaye
Büyük bir e-ticaret sisteminde geliştiriciler, finalize() yöntemini kullanarak geçici dosyaların otomatik olarak temizlenmesini umuyorlardı ama bu yöntemin nadir çağrılması nedeniyle, önbellek geçici dosyalarla doldu ve bu da disk yetersizliğine ve kesintilere yol açtı.
Hikaye
Yüksek trafikli bir REST API'de, büyük nesnelere yapılan referansları saklayan bir iç önbellek yanlışlıkla tutuldu ve bu bellek serbest bırakımını engelledi. Uygulama OutOfMemoryError ile çöktüğünde yapılan analiz, önbellekleme yönteminde bir hata olduğunu gösterdi.
Hikaye
Ağ bağlantılarını serbest bırakmak için finalize'lar kullanıldı ki bu "güvenilir bir yöntem" olarak düşünüldü. Bu, bağlantıların sistemde belirsiz bir süre sıkışmasına, blokajlara ve bağlantı havuzunun tükenmesine yol açıyordu.