Java'da String Pool Konsepti
Java programlama dilinde String veri tipi, çift tırnak içerisine yazılmış karakterler dizisi olarak tanımlanır. Aşağıda en basit haliyle bir String tanımlaması yapılmıştır.
String name = "Muhammet";
Yukarıdaki tanımlama String Literals olarak adlandırılmaktadır. String literals, bir programcının belirli bir değeri ifade etmek amacıyla yazdığı sabit karakter dizileridir.
Yukarıda String türünde tanımlanan ve name olarak adlandırılan nesne elbette primitive tiplerden(int,byte,long vb.) farklı olarak Stack yerine Heap bölgesinde tutulmaktadır, dolayısıyla bir referansa sahiptir. Yani derleyici bu satırı çalıştırdığında Heap bölgesinde “Muhammet” değerini tutan bir obje oluşturur ve “name” isimli değişkene bunun referansını verir.
Peki devam eden kısımda aşağıdaki gibi aynı değere sahip ikinci bir değişken tanımlaması yapsaydık bu iki nesnenin referansı aynı mı olurdu? Bunu anlamak için referanslarının hashCode değerlerini ekrana yazdıralım.
String name = "Muhammet";
String name2 = "Muhammet";
System.out.println(System.identityHashCode(name));
System.out.println(System.identityHashCode(name2));
//Output
//1705736037
//1705736037
Evet! Görüldüğü üzere her iki nesnenin de referanslarının hashCode değerlerini ekrana yazdırdığımızda aynı değeri yazdığını görüyoruz. Yukarıdaki örnekte iki tane değil bir milyon tane değişken de tanımlasaydık bunların tamamının referansı aynı olacaktı.
Buradan anlıyoruz ki biz bir milyon tane değişken tanımlasak da JVM bunlara karşılık Heap bölgesinde yalnızca bir tane obje tanımlıyor.
Peki Neden?
Tabii ki amaç bellek optimizasyonu. Varsayılan olarak literals kullanarak bir String objesi oluşturduğunuzda JVM oluşturmak istediğiniz değerin varlığını String Pool yapısı içerisinde kontrol eder. Eğer burada zaten bu değer mevcut ise, yeni bir tane oluşturmak yerine geriye bu objenin referansını döner. Bu sayede tekrar eden verinin önüne geçilir.
Nedir bu String Pool?
Makalemizin başlangıcındaki görselden görüldüğü üzere JVM Heap bellek kısmı içerisinde String Pool adı verilen bir alan vardır. Bu alan aynı zamanda String Constant Pool veya String Intern Pool olarak da adlandırılır.
Siz bir String oluşturduğunuzda JVM varsayılan olarak önce bu alanı kontrol eder. Eğer bu alanda, oluşturduğunuz String değişken içerisindeki değer zaten daha önce tanımlanmış ise onun referansı değişkeninize atanır. Eğer öncesinde böyle bir değer String Pool içerisinde tanımlanmamış ise String Pool içerisinde bu değer tanımlanır ve değişkeninize atanır.
Bundan sonra oluşturacağınız tüm String değişkenlerin değerleri bu alandan gelir veya bu alan içerisine tanımlanırlar. Bu sayede tekrar eden verinin önüne geçilmiş olur ve bellek kullanımı önemli ölçüde azaltılmış olur.
Peki String Pool Dışında String Tanımlamak İstersek?
String Pool içerisinde işleri yürütmenin getirdiği bellek kullanımında tasarruf avantajının yanısıra her yeni String değişken oluşturduğunuzda bu alanın baştan sonra kontrol edilmesi gibi bir search işlemi gerçekleşir. Kimi zaman bellekten tasarruf etmek yerine buradaki işlem’in gecikmesinden, yükünden kaçınmak isteyebiliriz.
Böyle durumlarda String Pool dışında, doğrudan Heap içerisinde String değişkenlerimizi tanımlamak, yönetmek isteyebiliriz. Bunun için tek yapmamız gereken tanımlama yaparken new anahtar kelimesini kullanmak.
String name = "Muhammet";
String name2 = "Muhammet";
String name3 = new String("Muhammet");
System.out.println(System.identityHashCode(name));
System.out.println(System.identityHashCode(name2));
System.out.println(System.identityHashCode(name3));
//Output
//1705736037
//1705736037
//455659002
Yukarıdaki örnekte görüldüğü üzere new anahtar kelimesi kullanılarak String değişken tanımlandığında, farklı bir referansa sahip String Pool dışında tanımlamış oluyoruz.
String name = "Muhammet";
String name2 = "Muhammet";
String name3 = new String("Muhammet");
System.out.println(name == name2);
System.out.println(name == name3);
System.out.println(name.equals(name3));
//Output
//true
//false
//true
Bu değişkenleri “==” operatörü kullanarak karşılaştırdığımızda, String Pool dışında olan ile içinde olan iki farklı değişken için sonuç false dönerken, String Pool içerisinde olan iki farklı değişken için sonuç true dönüyor.
Bunun sebebi == operatörü kullanarak yapılan karşılaştırmalar değerler üzerinden değil referanslar üzerinden yapılır. Dolayısıyla String Pool içerisindeki aynı değere sahip değişkenlerin referansı aynı iken, new anahtar kelimesi ile tanımlanan değişkenin referansı farklı.
Fakat referansları üzerinden değil değerleri üzerinden karşılaştırmak istersek de equals metodunu kullabiliriz.
intern() Fonksiyonu
String name4 = new String("Java");
String name5 = name4;
System.out.println(System.identityHashCode(name4));
System.out.println(System.identityHashCode(name5));
//Output
//250421012
//250421012
Yine kodlar üzerinden ilerleyelim. Yukarıdaki örnekte name5 isimli değişkeni name4 değişkeninden türetiyoruz. Dolayısıyla referansları aynı. Fakat String Pool içerisine alınmıyorlar. Çünkü Heap üzerinde yaratıldı, Heap üzerinden referans alındı.
Fakat bunun yerine oluşturacağımız yeni nesneyi String Pool içerisine koymak istiyor olabiliriz. Bu durumda .intern() fonksiyonu devreye giriyor.
String name4 = new String("Java");
String name5 = name4;
String name6 = name4.intern();
String name7 = "Java";
System.out.println(System.identityHashCode(name4));
System.out.println(System.identityHashCode(name5));
System.out.println(System.identityHashCode(name6));
System.out.println(System.identityHashCode(name7));
//Output
//250421012
//250421012
//1915318863
//1915318863
Yukarıda “name6” isimli değişkeni oluştururken name4 değişkenini kullandık. Bu değişken String Pool’da değil, Heap üzerinde kendi başına bulunuyordu. Fakat bundan bir değişken oluştururken .intern() metodunu kullanarak oluşturduk. Bu metod sayesinde eğer String Pool’da böyle bir değer yoksa oluştur ve String Pool’a ekle demiş olduk.
Devamında “Java” değerine sahip bir String değişkeni String literals kullanarak tanımladığımızda referansının String Pool’dan geldiğini görebiliyoruz.
Gelelim String Pool kavramının artı ve eksilerine;