5 Nisan 2012 Perşembe

JAVA Bytecode Ayrıntılı İnceleme - 2 (String Ekleme / Birleştirme / Concatenation)

JAVA Bytecode Nasıl Elde Edilir? başlıklı  ve JAVA Bytecode Ayrıntılı İnceleme başlıklı önceki yazılarımda, hazırlamış olduğum ByteCodeTest isimli sınıfın JAVA bytecode'unu nasıl elde edeceğimizi ve JAVA kodlarına karşılık gelen JAVA bytecode'larını incelemiştim.

Şimdi ise ByteCodeTest.java sınıfına bir method daha ekleyip string ekleme / birleştirme (string concatenation) işlemini JAVA bytecode'ları yardımıyla inceleyeceğim ve basit ama önemli bir konudan bahsedeceğim... (Devam)

ÖNEMLİ NOT : Bloglarda yazılan yazıları kopyalayıp kendi sitenizde, forumlarda yayınlamak çok hoş gibi görünse de aslında makaleyi yazan kişinin cesaretini kıran, motivasyonunu yok eden bir davranıştır. Burada yazılan yazıların da bir kısmını ya da tamamını kaynak göstermeden kopyalayan kişi düdüklü tenceredir, Sarkozy'dir.

ByteCodeTest.java sınıfımız şöyle idi:
public class ByteCodeTest {

    int i;

    public ByteCodeTest() {
        i = 0;
    }

    public void artir() {
        i = i + 1;
    }

    public void artirAlternatif() {
        i++;
    }
}
Şimdi, bu sınıfa selamVer() methodumuzu ekliyorum:
public void selamVer() {
        String s = "s" + "e" + "l" + "a" + "m";
        s += "sana";
        s += "arkadas";

        System.out.println(s);
}
Bu methodun JAVA bytecode'una baktığımızda ise:
public void selamVer();
    Code:
       0: ldc           #3                  // String selam
       2: astore_1     
       3: new           #4                  // class java/lang/StringBuilder
       6: dup          
       7: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
      10: aload_1      
      11: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      14: ldc           #7                  // String sana
      16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: astore_1     
      23: new           #4                  // class java/lang/StringBuilder
      26: dup          
      27: invokespecial #5                  // Method java/lang/StringBuilder."
<init>":()V
      30: aload_1      
      31: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      34: ldc           #9                  // String arkadas
      36: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      39: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      42: astore_1     
      43: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
      46: aload_1      
      47: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      50: return
Şeklinde uzunca bir komut listesi çıkıyor karşımıza (bazı komutlar sayfaya sığmadığından aşağıdaki satıra taşıyorlar).

Dikkat çeken ilk şey, JAVA compiler'ın
String s = "s" + "e" + "l" + "a" + "m";
şeklinde anlamsız bir şekilde atama yaptığımız satırı
0: ldc           #3   // String selam
haline getirebilecek kadar akıllı olmasıdır.

Buradan sonra işler biraz karışıyor.  3: no'lu satırda gördüğümüz
 3: new           #4   // class java/lang/StringBuilder
komutu bize isteğimiz dışında bir StringBuilder nesnesi oluşturulduğunu ve sonraki satırlarda constructor'ının çağırıldğını,
      11: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
komutuyla StringBuilder sınıfının append() methodunun çağırıldığını,
      14: ldc           #7                  // String sana
      16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: astore_1
satırları ile "sana" diye yeni bir string değişkeninin oluşturulduğunu, append() methodu ile birleştirildiğini toString() ile string'e dönüştürülüp yeni bir string olarak saklandığını;
      23: new           #4                  // class java/lang/StringBuilder
ile bir StringBuilder nesnesi daha oluşturulduğunu
      34: ldc           #9      // String arkadas
satırı ile "arkadas" diye yeni bir string değişkeninin oluşturulduğunu, append() methodu ile birleştirildiğini toString() ile string'e dönüştürülüp yeni bir string olarak saklandığını görüyoruz.

Yani buradan anlayacağımız, basit bir string ekleme işlemi için, isteğimiz dışında, bir sürü StringBuilder nesnesi oluşturuluyor, sık sık append() ve toString() methodları çağırılıyor ve bir sürü yeni string oluşturuluyor. Bu işlemi yüzlerce defa tekrarladığımız bir yazılım yazdığımızı düşünmek bile garip geliyor.

Peki, arka planda neler döndüğünü anladıktan sonra, aynı işlevi yerine getiren daha temiz bir kod yazabilir miyiz? Bir deneyelim...

Bu sınıfa selamVerAlternatif() isimli yeni methodumuzu ekliyorum:
public void selamVerAlternatif() {
    StringBuilder sb = new StringBuilder("s" + "e" + "l" + "a" + "m");
    sb.append("sana");
    sb.append("arkadas");

    System.out.println(sb.toString());
}
Burada yaptığımız şey StringBuilder nesnesinden bir örnek oluşturup append() ve toString() methodlarını ihtiyacımız olan yerlerde kullanmaktan başka bir şey değil.

JAVA bytecode'una bakalım:
public void selamVerAlternatif();
Code:
   0: new           #4                  // class java/lang/StringBuilder
   3: dup          
   4: ldc           #3                  // String selam
   6: invokespecial #12                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   9: astore_1     
  10: aload_1      
  11: ldc           #7                  // String sana
  13: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  16: pop          
  17: aload_1      
  18: ldc           #9                  // String arkadas
  20: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  23: pop          
  24: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
  27: aload_1      
  28: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  31: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  34: return
Yani, tam ihtiyaç duyduğumuz şekilde, StringBuilder nesnesinden bir örnek oluşturup iki defa append() ve bir defa da toString() methodlarının kullanıldığını görebiliyoruz. Bu iyileştirmenin, daha az bellek işlemi gerçekleşiyor olacağından, yazılımımıza hız katacağı da aşikar.

Daha önce dediğim gibi, JAVA bytecode hakkında hiçbir şey bilmeden, sadece, StringBuilder kullanmanın daha iyi olacağını birisinden duyduğunuz için, yukarıdaki satırları aynen yazıyor olabilir hatta daha iyisini de yapıyor olabilirsiniz. Ancak JAVA bytecode'unu aslında neler olup bittiğini anlamak için kullandığınız zaman siz hep bir adım önde olursunuz.

JAVA bytecode serisine bu yazı ile son vermeyi düşünüyorum.




Hiç yorum yok:

Yorum Gönder