Sunday, January 18, 2009

Java IntegerCache

Şirkette yazdığımız bir kodu Eclipse FindBugs plugini ile incelerken kodun sarı renkli böcekler tarafından istila edildiğini gördük. Integer sınıfını kullandığımız hemen hemen her sınıf içinde FindBugs bize aşağıdaki uyarıyı veriyordu:
Method invokes inefficient Number constructor; use static valueOf instead

"Yav bırak allah aşkına, alt tarafı bir Integer yaratacaksın" deyip FindBugs uyarısını görmezden gelme varsayılan davranışımdır fakat bugün can sıkıntısının da etkisi ile Java SDK içindeki Integer sınıfının koduna baktım. "new Integer(5)" ile "Integer.valueOf(5)" arasında ne gibi bir fark olabilir çok merak ediyordum. Integer.java sınıfı içerisinde fark yaratan kod aşağıdaki IntegerCache sınıfıymış. JVM sizin için 256 adet tam sayıyı önbelleğe alıyor. -128'den +127'ye kadar olan tam sayılar için Integer.valueOf size hep aynı Integer instance'ını veriyor.
private static class IntegerCache {
private IntegerCache(){}

static final Integer cache[] = new Integer[-(-128) + 127 + 1];

static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Integer(i - 128);
}
}

/**
* Returns a Integer instance representing the specified
* int value.
* If a new Integer instance is not required, this method
* should generally be used in preference to the constructor
* {@link #Integer(int)}, as this method is likely to yield
* significantly better space and time performance by caching
* frequently requested values.
*
* @param  i an int value.
* @return a Integer instance representing i.
* @since  1.5
*/
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache 
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
Küçük bir örnekle konuya nokta koymak istiyorum. Bğyle ufak tefek performans optimizasyonları ile işim olmaz fakat java 1.5'ten beri aktif olan bu özelliği daha yeni öğrendiğim için ilgimi çekti sizinle de paylaşmak istedim.
public class IntegerCacheTest {
public static void main(String[] args) {
Integer int1 = Integer.valueOf(5);
Integer int2 = Integer.valueOf(5);
Integer int3 = new Integer(5);
Integer int4 = 5; 
if ( int1 == int2) {
System.out.println("int1 ve int2 ayni instance.");
} 
if( int1!= int3) {
System.out.println("Fakat int1 ve int3 farkli instance'lar");
} 
if (int1 == int4) {
System.out.println("int1 ve int4 de aynı instance");
} 
if (int1.equals(int2) && int2.equals(int3) && int3.equals(int4)) {
System.out.println("ve son olarak hepsinin değeri aynı :)");
}
}
}
Yukarıdaki kod parçasını çalıştırırsanız aşağıdaki gibi bir çıktı alırsınız.
int1 ve int2 ayni instance.
Fakat int1 ve int3 farkli instance'lar
int1 ve int4 de aynı instance
ve son olarak hepsinin değeri aynı :)
Fakat bu sonuç sizi beni şaşırttığı kadar şaşırtmayabilir :)

1 comments:

talip ozturk said...

JVM'ler gun gectikce daha akilli performans optimizasyonlari yapiyorlar. Mesela aslinda, Java Runtime'in cok kullanilan Integer'lari otomatik cache lemesi mumkun. Integer'in immutable ve final olmasi JVM icin cok buyuk bir ipucu. hatta bu IntegerCache'ten cok daha etkili olacaktir cunku IntegerCache sadece -128 ile 127 arasindaki integer'lari cache liyor.

aslinda Java runtime bunu her key-value pair'e dunusturebilecegi immutable ve final olan instancelar icin yapmasi mumkun. bu tur class'lar icin code'lanmis internal cachelere ihtiyac olmamali.

selamlar,
-talip