Java’da Performans Arttırıcı Yollar - Bölüm 5
10 Ocak 2006 tarihli, Java, Programlama köşesine ait yazı.
“Thirty Ways to Improve the Performance of Your Java Programs” çevirisinin son yazısında disk ve belleği nasıl verimli kullanacağımız anlatılıyor. Böylelikle 5 bölümlük çeviri de tamamlanmış oluyor.
7. Verimli Disk ve Bellek Kullanımı
Bu bölümde Java programlarınızda disk ve bellek kullanımını nasıl verimli bir şekilde azaltacağınız hakkında birkaç teknik gösterilecektir.
7.1 .class Dosya Boyutu
Java derleyiciler nesnenin metodlarının ve attribute’larını bytecode olarak .class dosyasına kaydederler. Programınızı derlerken .class dosyasının ne tür bilgiler içereceğini isterseniz ayarlayabilirsiniz. Code gerçek bytecode’u, SourceFile derlenen asıl dosya hakkında bilgileri, LineNumberTable bytecode satırlarının asıl java dosyasındaki karşılıklarını, LocalVariableTable ise yerel değişkenlerin stack’teki offset’lerini belirtiyor. Farklı derleme parametreleriyle hangi özellikler .class dosyanıza ekleniyor bilgisi aşağıda:
javac –g Code, SourceFile, LineNumberTable, LocalVariableTable
javac -g:none Code
Peki bu .class dosyasının boyutunu nasıl etkileyecek? JDK 1.2.2 kaynak kodlarından java.io, java.lang, ve java.util kütüphanelerinin farklı parametrelerle derlenmiş boyutları şöyle:
javac –g 815K
javac –g:none 550K
Sadece kod eklenmiş .class dosyasın yaklaşık olarak %20 oranında küçülüyor. Bunu sağlayan bazı ücretli araçlar da mevcut. Ancak mesela satır numaralarını dahil etmediğiniz takdirde debugging ve exception raporlama konusunda bazı problemler çıkabiliyor.
7.2 Wrapper Class’lar
Java’da primitive veri tiplerini (int ve double gibi mesela) obje olarak tutmamızı sağlayan wrapper class‘lar mevcut. Mesela şöyle:
Wrapper class’ların büyük eksisi hem performanstan, hem yavaşlıklarından, hem de bellekte kapladıkları yerden verimsiz oluşudur. Primitive veri tiplerini kullanmak her zaman için daha avantajlıdır.
7.3 Garbage Collection
Java’nın otomatik bellek yönetimini sağlayan garbage collection adlı bir mekanizması vardır. Basitçe yaptığı iş bellekte yer işgal eden, fakat kullanılmayan objeleri boş alan olarak tekrar geri döndürmek. Normalde bu işlem için kaygılanmanıza gerek yok, ancak bazı özel durumlarda bu işlemin doğru çalışmasına yardımcı olabilirsiniz. Mesela düşünün ki programınızda obje referansları içeren bir stack var ve siz en üstteki objeyi pop edecek bir metod yazdınız:
return stack[stackp–];
}
Bu metod doğru çalışıyor gibi görünüyor, fakat yeni pop ettiğimiz stack bölümü sorun yaratmaz mı? Normalde kullanmadığımız bu bölüm halen bir objeye (ki belki büyük bir array’e) referans tutuyor. Aynı referans bu metodu çağıran metoda gönderiliyor ve belki de değerlendirilip bu objenin artık kullanılmayacağı anlaşılıyor. Tabii ki bu aşamada garbage collection mekanizmasının çalışıp kullanılmayan bu büyük objenin belleğe geri döndürülmesi lazım, ancak durun bir dakika, halen stack’te bu objeye bir referans var! Metodu şu şekilde yazarsak sanırım sorun çözülüyor:
Object tmp = stack[stackp];
stack[stackp–] = null;
return tmp;
}
Başka bir değişle kullanılmayacak referans null olarak atanıyor. Bu tekniğin farklı yerlerde kullanılmasının bellek geri-dönüşümüne büyük katkı sağlayacağından eminim.
7.4 SoftReference
java.lang.ref.SoftReference class’ı akıllı bellek alanları yaratmak için kullanılan bir nesnedir. Akıllı bellek alanı derken kastettiğimiz belli bir veri tipi, mesela diskten okuduğumuz bir dosya için gerektiğinde belleğe dönebilecek bir referans oluşturmaktır. Garbage collector OutOfMemoryError vermeden önce bu tip verilere bakar ve ek bellek alanı yaratır. Nasıl çalıştığını anlamak için büyük bir array’e point eden bir obje referansı düşünün:
ama bu array’i çok darda kalmadığınız sürece bellekte tutmak istiyorsunuz. Bunun için softreference kullanmanız gerekiyor:
Böylece ileride bellek hata verecek düzeyde azaldığı takdirde softreference boş bellek alanı olarak geri döndürülebilecek. Tabii softreference kullanırken önce bir test etmeniz gerekiyor:
(obje geri döndürülmüş)
else
(obje halen kullanılabilir, yani bellekte)
Bu yöntem bellekte büyük yer kaplayan, yalnız gerektiğinde belleğe geri döndürülmesi sorun yaratmayacak veriler için kullanılmalıdır.
7.5 BitSet
BitSet class’ı bit array’lerini verimli bir şekilde tanımlamak için kullanılır (byte ile bit’i karıştırmamaya dikkat edin!). Çok basit bir programla açıklayabiliriz:
public class size_bitset {
public static void main(String args[]) {
BitSet b = new BitSet();
b.set(37);
b.set(59);
b.set(73);
System.out.println(b);
}
}
programının çıktısı şu şekilde olacaktır:
BitSet ile basit bir boolean array iki yönden farklıdır: (1) BitSet dinamiktir, yani istediğiniz zaman bit ya da bit’ler ekleyebilirsiniz; (2) BitSet belleği daha verimli kullanır, boolean array’i elemanları saklamak için byte array şeklinde tanımlanmış olabilir, ki bu bir bit için 8 bit’lik yer kullanmak anlamına gelir.
7.6 Sparse (Dağınık) Array Tanımlama
Düşünün ki çok büyük bir array ile çalışmak istiyorsunuz, ancak bellekte bu büyüklükte bir boş alan bulamayacağınızı düşünüyor, belki de büyük ama dağınık yerlerde saklanan bir array kullanıp belleği verimli kullanmak istiyorsunuz. Bu işi yapmanın bir yolunu aşağıda gösteriyoruz:
public class SparseArrayList extends java.util.AbstractList {
private static final int PAGE_SIZE = 1024; /* sayfa uzunluğu */
private Object pages[][] = new Object[0][]; /* sayfalar *//* constructor metodu */
public SparseArrayList() {}
/* bellekte ayrılan toplam obje sayısını döndüren metod */
public int size() {
return pages.length * PAGE_SIZE;
}
/* istenilen array pozisyonuna verilen objeyi atayan metod */
public Object set(int index, Object val) {
if (index < 0)
throw new IllegalArgumentException();
int p = index / PAGE_SIZE;
if (p >= pages.length) { /* gerekiyorsa sayfa sayısını artır */
Object newpages[][] = new Object[p + 1][];
System.arraycopy(pages, 0, newpages, 0,
pages.length);
pages = newpages;
}
if (pages[p] == null) /* bellekte yeni sayfa için alan ayır */
pages[p] = new Object[PAGE_SIZE];
Object old = pages[p][index % PAGE_SIZE];
pages[p][index % PAGE_SIZE] = val;
return old;
}
/* verilen pozisyondaki objeyi döndüren metod */
public Object get(int index) {
if (index < 0)
throw new IllegalArgumentException();
int p = index / PAGE_SIZE;
if (p >= pages.length || pages[p] == null)
return null;
return pages[p][index % PAGE_SIZE];
}
}
SparseArrayList class’ımız AbstractList‘i extend eder, yani bir List olarak kullanabiliriz. Bu kodda array 1024′lük parçalara ayrılıp bellekte bu şekilde saklanır. Verilen array pozisyonu sayfa numarası (page) ve sayfa adresi (offset) olarak çevirildikten sonra asıl obje referansı iki boyutlu array’den bulunur. Örnek olarak 1030′uncu array elemanı için sayfa 1 ve offset 6′dır.SparseArrayList kullanımına bir örnek ise şöyle verilmiş:
public class size_driver {
public static void main(String args[]) {
SparseArrayList list = new SparseArrayList();
Random rn = new Random();
for(int i=1;i<= 1000000; i++) {
int r = rn.nextInt(1000000);
list.set(r, new Integer(r));
Integer iw = (Integer)list.get(r);
if (iw.intValue() != r)
System.err.println(”error”);
}
}
}
Bu test programı SparseArrayList kullanarak array’e rastgele sayılar atayıp sonra da atamanın doğru yapılıp yapılmadığını kontrol ediyor. İsterseniz kendi ihtiyaçlarınıza göre bu tarz array’ler tanımlayıp belleği verimli şekilde kullanabilirsiniz.
Yorumlar - Başa Dön
12 Ocak 2006
Çevirine emeğine sağlık diyorum. Senin gibi insanlar çok yok valla.