JVM Nasıl Çalışır Yazı Serisi – Java Just In Time Compiler (JIT) Nasıl Çalışır?

Java’yı çoğu programcı yorumlanan (interpreted) dil olarak bilir. Java’nın yavaş olduğu efsanesi de başlangıcını da burada bulur. Bytekod olarak derlenen Java sınıfları Java sanal makinesi (Java Virtual Machine – JVM) bünyesinde yorumlanır. Tek derleme işlemi Java sınıflarının bytekoda dönüştürülmesi esnasında yapılmaz. JVM bünyesinde de bytekodun makine koduna dönüştürüldüğü bir derleme gerçekleştirilir. Bu işleme Just in time (JIT) compilation ismi verilmektedir. Bu yazımda JVM bünyesinde kodun nasıl derlendiğini örnekler üzerinden aktarmak istiyorum.

Derleme (compilation) sürecini yüksek dillerde yazılan program kodlarının işlemcinin (CPU) anladığı makine koduna dönüştürüldüğü ve bu şekilde CPU üzerinde koşturulabilir hale getirildiği işlem olarak tanımlayabiliriz. Derleme işlemini gerçekleştiren programları yani derleyicileri kabaca iki guruba ayırmak mümkün. Bunlar:

  • Ahead of time compiler (AOT)
  • Just in time compiler (JIT)

C/C++ gibi diller AOT tarzı derleciyiler kullanırlar. AOT derleyicileri program kodunu programın koşturulmasından önce kullanılan donanımın anladığı makine koduna dönüştürürler. JIT ise Java gibi dillerde uygulamanın çalışması esnasında (runtime) kazanılan performans verileri doğrultusunda uygulanan derleme tekniğidir. JIT devreye girmeden önce kod bytekod olarak yorumlanır. AOT ile derlenen uygulamalar JIT ile derlenen uygulamalara kıyasla daha hızlı çalışırlar, çünkü kod tamamıyla işlemcinin anladığı şekilde derlenmiştır ve yorumlanmadan doğrudan işlemci bünyesinde koşturulur. Buna karşın AOT ile derlenen uygulamalar derlendikleri platformlara bağımlıdırlar ve başka donanım türlerinde koşturulamazlar. Bunun için yeniden derleme işleminin gerçekleştirilmesi gerekmektedir. JIT derleyicileri platformdan bağımsız çalışırlar.

Java kodları javac ile derlenir. Bu derleme işlemi sonucunda bytekod oluşur. Aşağıdaki örnekte HelloWorld sınıfının nasıl derlendiğini ve derlenen sınıfın hangi bytekodu ihtiva ettiğini görmekteyiz.

[source language=”java”]

C:\>java HelloWorld

public class HelloWorld {

public static void main(String[] args){
System.out.println("Merhaba Dünya!");
}
}
[/source]

[source language=”java”]
C:\>javap -c HelloWorld.class

Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: retur

public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Merhaba D?nya!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
[/source]

Bir Java uygulaması JVM bünyesinde çalışır hale getirildiğinde, yukarıda yer alan bytekod JVM tarafından yorumlanmaya başlar. JVM bu durumda “interpreted” modundadır ve bytekodunu satır, satır yorumlar. Bu haliyle JVM bir durum makinesidir (state machine).

JVM bytekodu yorumlarken kodun sık kullanılan alanları hakkında istatistiki bilgiler toplar. Bu şekilde örneğin hangi metodun hangi sıklıkla koşturulduğunu ölçmek mümkündür. JVM parametreleri aracılığı ile JIT in derleme işlemine geçtiği barajlar tanımlanabilir. Örneğin bir metot on bin kez koşturulduktan sonra, bu metodun JIT tarafından derlenmesi ve bytekoddan makine koduna dönüştürülmesi sağlanabilir. Bu değeri örneğin bin olarak değiştirmek de mümkündür.

JIT ile derleme işlemi gerçekleştikten sonra JVM “compiled” moduna geçer. Bu modda JVM işlemciye (CPU) has işlemleri (native direct operations) gerçekleştirir. Kod artık yorumlanmadığı için daha hızlı çalışmaktadır. JVM bir state machine olmaktan çıkmış ve artık doğrudan işlemci üzerinde işlem yaptığından bir native register machine haline gelmiştir.

JVM in yorumlama, derleme ve uygulamayı koşturma esnasında içinde bulunduğu durumları aşağıdaki diyagramda görmekteyiz.

[source language=”java”]

(1) Interpreted –> (2)Profiline
^ |
| |
| v
(4) Deoptimize <– (3)JIT Compiled

[/source]

JVM başlangıçta interpreted (1) yani bytekodu yorumlayıcı durumundadır. Kod yorumlanırken JVM tarafından profiling (2) tekniği ile ölçümler gerçekleştirlir. Bu veriler baz alınarak kodun hangi bölümlerinin JIT ile derleneceği kararı alınır ve kod derlenir. JIT derleme (3) işlemi esnasında tüm bilgilere sahip olamayacağından bazı varsayımlarda bulunur ve spekülasyona dayalı kararlar neticesinde derleme işlemini gerçekleştirir. Bazı şartlar altından bu varsayımlar geçerliliklerini kaybettiklerinden, derlenen kod işe yaramaz hale gelir ve bir kenara bırakılarak (4), yeniden yorumlama (1) moduna geçilir. Bu döngü uygulamanın hayatı boyunca devam eder. Belli bir kullanım süresinden sonra kod hakkında toplanan bilgiler yeterli olduğundan, kodun çok kullanılan alanları derlenmiş olur ve bu da Java uygulamalarını C/C++ uygulamaları gibi hızlı kılar. JIT derleyicisi bir AOT derleyicisine nazaran daha fazla runtime istatistik bilgisine sahip olduğundan, kodun işlemci için optimize edilme işlemi daha verimli sonuçlar verir. Bu yüzden Java uygulamalarının C/C++ uygulamalarına nazaran yer yer daha hızlı çalıştığı söylenebilir. Diğer bir yazımda JIT tarafından uygulanan kod optimizasyon tekniklerine değineceğim.

Aşağıda yer alan Calculator sınıfı aracılığı ile JIT tarafından derleme işleminin nasıl yapıldığını inceleyebiliriz.

[source language=”java”]
package com.kurumsaljava.jvm.jit;

public class Calculator {

public static void main(String[] args) {
int max = Integer.parseInt(args[0]);
System.out.println(addAll(max));
}

private static int addAll(int max) {
int result = 0;
for (int i = 0; i < max; i++) {
result = add(result, i);
}
return result;
}

private static int add(int result, int i) {
return result + i;
}
}
[/source]

JIT tarafından yapılan derleme işlemini takip edebilmek için -XX:+PrintCompilation parametresini aşağıdaki şekilde kullanmamız gerekiyor. Bunun yanı sıra -server parametresinin kullanılması, JIT derleyicisinin daha agresif bir şekilde derleme işlemine girişmesini mümkün kılacaktır.

[source language=”java”]
c:\>java -server -XX:+PrintCompilation com.kurumsaljava.
jvm.jit.Calculator 100000
[/source]

Calculator sınıfı 1 – 100000 arasındaki rakamları toplamakta ve neticeyi ekranda görüntülemektedir. Ekran çıktısı şu şekilde olacaktır:

[source language=”java”]
1 80 1 3 java.lang.Object::<init> (1 bytes)
2 82 2 3 java.lang.String::hashCode (55 bytes)
3 83 3 3 java.lang.String::charAt (29 bytes)
4 86 4 3 java.lang.Character::toLowerCase (9 bytes)
5 86 5 3 java.lang.CharacterData::of (120 bytes)
6 86 6 3 java.lang.CharacterDataLatin1::toLowerCase (39 bytes)
7 86 7 3 java.lang.CharacterDataLatin1::getProperties (11 bytes)
8 86 8 3 java.lang.String::indexOf (70 bytes)
9 87 9 3 java.util.Arrays::copyOfRange (63 bytes)
10 87 10 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes)
11 88 11 n 0 java.lang.System::arraycopy (native) (static)
12 88 12 3 java.lang.AbstractStringBuilder::append (29 bytes)
13 88 13 3 java.io.WinNTFileSystem::isSlash (18 bytes)
14 88 14 s 3 java.lang.StringBuffer::append (13 bytes)
15 89 15 3 java.lang.String::length (6 bytes)
16 89 16 1 java.lang.Object::<init> (1 bytes)
17 89 1 3 java.lang.Object::<init> (1 bytes) made not entrant
18 89 17 3 java.lang.String::getChars (62 bytes)
19 91 18 3 java.lang.Math::min (11 bytes)
20 97 19 3 sun.misc.FDBigInteger::mult (64 bytes)
21 97 20 3 sun.misc.FDBigInteger::<init> (30 bytes)
22 98 21 3 sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
23 98 22 3 sun.misc.FDBigInteger::mult (44 bytes)
24 100 23 n 0 java.lang.System::nanoTime (native) (static)
25 100 24 3 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
26 100 25 1 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
27 100 24 3 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes) made not entrant
28 105 26 3 java.lang.AbstractStringBuilder::append (50 bytes)
29 110 27 % 3 com.kurumsaljava.jvm.jol.Calculator::addAll @ 7 (75 bytes)
30 111 28 3 com.kurumsaljava.jvm.jol.Calculator::addAll (75 bytes)
31 113 29 3 sun.nio.cs.SingleByte$Encoder::encode (32 bytes)

704982704
[/source]

Birinci kolonda satır numaralarını, ikinci kolonda JVM in işlem için kullandığı zamanı (millisecond), üçüncü kolonda derleme sırasını, dördüncü kolonda derleyici seviyesini, beşinci kolonda derlenen metodu görmekteyiz. Bizim için ilginç olan derleme işlemleri aşağıda yer almaktadır:

[source language=”java”]
25 100 24 3 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
26 100 25 1 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
27 100 24 3 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes) made not entrant
29 110 27 % 3 com.kurumsaljava.jvm.jol.Calculator::addAll @ 7 (75 bytes)
30 111 28 3 com.kurumsaljava.jvm.jol.Calculator::addAll (75 bytes)
[/source]

25. ve 27. satırlarda add() metodunun derlendiğini ve derleme neticesinin 4 byte büyüklükte olduğunu görmekteyiz. 27. satırda “made not entrant” ibaresi yer almakta. Bu JIT in kullandığı varsayımların geçerliliğini yitirdiği ve derlenen kodun devre dışı kaldığı anlamına gelmektedir. 27. satırda JVM deoptimizasyon moduna geçerek, metodun yeni bilgiler doğrultusunda yeniden derlenmesinin önünü açmaktadır.

Derleme sırasının düzenli olmadığını görmekteyiz. Bu JIT derleyicisinin arka planda thread bazlı çalıştığının göstergesidir.

26. satırda add() metodu tekrar derlenmekte ve işlemci üzerinde makine kodu olarak koşturulabilir hale getirilmektedir. Ayrıca 29. ve 30. satırlarda addAll() metodunun da derlendiğini görmekteyiz. 29. satırda yer alan % isaretinin ne anlama geldiğine birazdan değineceğim.

Daha önce de bahsettiğim gibi JIT derleme işlemi esnasında kodu mümkün mertebe daha hızlı çalışabilsin diye optimize etme gayretindedir. Derleyiciler tarafından yapılan bu optimizasyonların başında method inlining gelir. Bu derleme esnasında bir metot gövdesinin kullanıldığı metota kod olarak eklenmesi anlamına gelmektedir. Örneğin aşağıda yer alan addAll() ve add() metotları JIT tarafından şu şekilde optimize edeilecektir:

[source language=”java”]
private static int addAll(int max) {
int result = 0;
for (int i = 0; i < max; i++) {
result = add(result, i);
}
return result;
}

private static int add(int result, int i) {
return result + i;
}
[/source]

JIT tarafından optimize edilen kod şu yapıpdadır:

[source language=”java”]
private static int addAll(int max) {
int result = 0;
for (int i = 0; i < max; i++) {
result += i;
}
return result;
}
[/source]

Görüldügü gibi JIT add() bünyesindeki kodu birebir addAll() metoduna taşıdı. Bu şekilde add() metoduna geçmek için gerekli frame (bknz. JVM Stack Nedir ve Nasıl Çalışır? başlıklı yazım) oluşturma işlemi gereksiz hale geldiğinden, JVM kodu daha hızlı koşturulabilmektedir.

Method inlining işleminin gerçekleştiğini ispatlamak amacıyla Calculator sınıfını aşağidaki şekilde değiştirerek, tekrar koşturuyorum:

[source language=”java”]
public class Calculator {

public static void main(String[] args) {
int max = Integer.parseInt(args[0]);
System.out.println(addAll(max));
}

private static int addAll(int max) {
int result = 0;
for (int i = 0; i < max; i++) {
long start = System.nanoTime();
result = add(result, i);
long duration = System.nanoTime() – start;
if (i % 10000 == 0)
System.out.println("add execution took " +
((duration) / 1000.0d) + " µs");
}

return result;
}

private static int add(int result, int i) {
return result + i;
}
}
[/source]

Ekran çıktısı şu şekilde olacaktır:

[source language=”java”]
80 1 3 java.lang.Object::<init> (1 bytes)
82 2 3 java.lang.String::hashCode (55 bytes)
83 3 3 java.lang.String::charAt (29 bytes)
86 4 3 java.lang.Character::toLowerCase (9 bytes)
86 5 3 java.lang.CharacterData::of (120 bytes)
86 6 3 java.lang.CharacterDataLatin1::toLowerCase (39 bytes)
86 7 3 java.lang.CharacterDataLatin1::getProperties (11 bytes)
86 8 3 java.lang.String::indexOf (70 bytes)
87 9 3 java.util.Arrays::copyOfRange (63 bytes)
87 10 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes)
88 11 n 0 java.lang.System::arraycopy (native) (static)
88 12 3 java.lang.AbstractStringBuilder::append (29 bytes)
88 13 3 java.io.WinNTFileSystem::isSlash (18 bytes)
88 14 s 3 java.lang.StringBuffer::append (13 bytes)
89 15 3 java.lang.String::length (6 bytes)
89 16 1 java.lang.Object::<init> (1 bytes)
89 1 3 java.lang.Object::<init> (1 bytes) made not entrant
89 17 3 java.lang.String::getChars (62 bytes)
91 18 3 java.lang.Math::min (11 bytes)
97 19 3 sun.misc.FDBigInteger::mult (64 bytes)
97 20 3 sun.misc.FDBigInteger::<init> (30 bytes)
98 21 3 sun.misc.FDBigInteger::trimLeadingZeros (57 bytes)
98 22 3 sun.misc.FDBigInteger::mult (44 bytes)
add execution took 1.579 µs
100 23 n 0 java.lang.System::nanoTime (native) (static)
100 24 3 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
100 25 1 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
100 24 3 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes) made not entrant
add execution took 0.0 µs
add execution took 0.395 µs
105 26 3 java.lang.AbstractStringBuilder::append (50 bytes)
add execution took 0.0 µs
add execution took 0.0 µs
add execution took 0.0 µs
add execution took 0.0 µs
110 27 % 3 com.kurumsaljava.jvm.jol.Calculator::addAll @ 7 (75 bytes)
111 28 3 com.kurumsaljava.jvm.jol.Calculator::addAll (75 bytes)
add execution took 0.0 µs
add execution took 0.0 µs
113 29 3 sun.nio.cs.SingleByte$Encoder::encode (32 bytes)
add execution took 0.0 µs
704982704
[/source]

İlk on bin adet add() koşturulması neticesinde toplam koşturma süresi 1.579 µs dır. Akabinde kodun iki sefer derlendiğini ve koşma süresinin 0.0 µs şeklinde değiştiğini görmekteyiz. Bu değer add() metodu inline edildiği zaman oluşabilecek bir değerdir. Add() metodu JIT tarafından “made not entrant” yapıldıktan yani derlenen kod çöpe atıldıktan sonra bir sonraki on bin adet koşturulması 0.395 µs süresinde gerçekleşmektedir. Bu add() metodunun deoptimizasyon sonucunda tekrar yorumlanmaya başlandığının ispatıdır. Add() daha sonra tekrar derlenerek, koşturma zamanı sıfıra düşmektedir, çünkü JIT tarafından tekrar addAll() bünyesinde inline edilmiştir.

Bu arada addAll() metodunun ilk derlendiği yerde % işareti görmekteyiz. Bu JIT in derleme işlemini “On stack replacement” modunda yaptığı anlamına gelmektedir. On stack replacement ne anlama gelmektedir? JIT genelde çok kullanılan metotları birebir derleme eğilimindedir. Derleme işleminden sonra derlenen metodu kullanan diğer metotlar yeni makine koduna işaret edecek şekilde yeniden yapılandırılır. Bunu bir hashtable bünyesinde yapılan bir eşleme olarak düşünebiliriz. Tüm kullanıcı metotlar derlenen metoda işaret ederler. JIT in metodu derleyebilmesi için metodun stack frame ini açmamış yani koşuyor durumda olmaması gerekmektedir. Bu add() metodu bünyesinde mevcut olan bir durumdur. Bu metot yüz bin kez koşturulmaktadır ve JIT bu metodu derlemek için gerekli fırsatları değerlendirmektedir. Lakin addAll() metoduna baktığımızda, bu metodun sadece bir kez koşturulduğunu görmekteyiz. Buna rağmen addAll() metodu JIT için bir sicak alandır (hotspot) ve derlenmelidir, çünkü uygulama için gerekli zamanın çok büyük bir kısmı bu metot bünyesinde geçmektedir.

JIT sadece bir kez koşturulan ama derlenmeyi hak eden metotları on stack replacemant yöntemi ile derlemektedir. Bu yöntemde tüm metot derlenmemektedir. JIT sadece for döngüsünün başlangıcından, sonuna kadar olan bölümü derler ve for döngüsünü koşturan yorumlayıcının (interpreter) sahip olduğu stack frame i derlenen koda giriş noktası olarak kullanır. Bu şekilde uzun döngülerin de derlenmesi mümkün hale gelmektedir.

JIT tarafından yapılan method inlining işlemini -XX:+PrintInlining parametresi ile takip etmek mümkündür. Şimdi uygulamayı bu yeni parametre ile çalıştırarak, hangi metotların inlining yöntemi ile yok edildiğini inceleylim. Bu parametreyi kullanabilmek için -XX:+UnlockDiagnosticVMOptions parametresi ile gerekli JVM kanallarının açılması gerekmektedir. Bu sebeple Calculator sınıfını aşağıdaki şekilde koşturmamız gerekmektedir:

[source language=”java”]
c:\>java -server -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions
-XX:+PrintInlining com.kurumsaljava.jvm.jit.Calculator 100000
[/source]

Ekran çıktısı şu şekilde olacaktır:

[source language=”java”]
65 1 3 java.lang.Object::<init> (1 bytes)
68 2 3 java.lang.String::hashCode (55 bytes)
68 3 3 java.lang.String::charAt (29 bytes)
@ 18 java/lang/StringIndexOutOfBoundsException::<init> (not loaded) not inlineable
71 4 3 java.lang.Character::toLowerCase (9 bytes)
@ 1 java.lang.CharacterData::of (120 bytes) callee is too large
@ 5 java.lang.CharacterData::toLowerCase (0 bytes) no static binding
71 5 3 java.lang.CharacterData::of (120 bytes)
71 6 3 java.lang.CharacterDataLatin1::toLowerCase (39 bytes)
@ 4 java.lang.CharacterDataLatin1::getProperties (11 bytes)
71 7 3 java.lang.CharacterDataLatin1::getProperties (11 bytes)
72 9 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes)
@ 12 java.lang.AbstractStringBuilder::expandCapacity (50 bytes) callee is too large
72 8 3 java.util.Arrays::copyOfRange (63 bytes)
@ 16 java.lang.StringBuilder::<init> (7 bytes)
@ 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
@ 1 java.lang.Object::<init> (1 bytes)
@ 20 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (62 bytes) callee is too large
@ 25 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (50 bytes) callee is too large
@ 29 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (62 bytes) callee is too large
@ 32 java.lang.StringBuilder::toString (17 bytes)
@ 13 java.lang.String::<init> (62 bytes) callee is too large
@ 35 java.lang.IllegalArgumentException::<init> (6 bytes) don’t inline Throwable constructors
@ 54 java.lang.Math::min (11 bytes)
@ 57 java.lang.System::arraycopy (0 bytes) intrinsic
73 11 n 0 java.lang.System::arraycopy (native) (static)
73 10 3 java.lang.String::indexOf (70 bytes)
@ 66 java.lang.String::indexOfSupplementary (71 bytes) callee is too large
73 12 3 java.lang.AbstractStringBuilder::append (29 bytes)
@ 7 java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes)
@ 12 java.lang.AbstractStringBuilder::expandCapacity (50 bytes) callee is too large
73 16 1 java.lang.Object::<init> (1 bytes)
73 1 3 java.lang.Object::<init> (1 bytes) made not entrant
73 13 3 java.io.WinNTFileSystem::isSlash (18 bytes)
73 14 s 3 java.lang.StringBuffer::append (13 bytes)
@ 7 java.lang.AbstractStringBuilder::append (29 bytes)
@ 7 java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes)
@ 12 java.lang.AbstractStringBuilder::expandCapacity (50 bytes) callee is too large
74 15 3 java.lang.String::length (6 bytes)
74 17 3 java.lang.String::getChars (62 bytes)
@ 9 java/lang/StringIndexOutOfBoundsException::<init> (not loaded) not inlineable
@ 27 java/lang/StringIndexOutOfBoundsException::<init> (not loaded) not inlineable
@ 43 java/lang/StringIndexOutOfBoundsException::<init> (not loaded) not inlineable
@ 58 java.lang.System::arraycopy (0 bytes) intrinsic
76 18 3 java.lang.Math::min (11 bytes)
78 19 3 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
79 20 1 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
79 19 3 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes) made not entrant
80 21 % 3 com.kurumsaljava.jvm.jol.Calculator::addAll @ 7 (23 bytes)
@ 9 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
81 22 3 com.kurumsaljava.jvm.jol.Calculator::addAll (23 bytes)
@ 9 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
704982704
[/source]

Bizim için ilginç olan bölümleri şu şekilde özetleyebiliriz:

[source language=”java”]

1) 78 19 3 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
2) 79 20 1 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
3) 79 19 3 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes) made not entrant
4) 80 21 % 3 com.kurumsaljava.jvm.jol.Calculator::addAll @ 7 (23 bytes)
5) @ 9 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
6) 81 22 3 com.kurumsaljava.jvm.jol.Calculator::addAll (23 bytes)
@ 9 com.kurumsaljava.jvm.jol.Calculator::add (4 bytes)
[/source]

5. satırda add() metodunun addAdll() metoduna taşındığını görmekteyiz. 3. satırda add() metodu depotimizasyon sonucu devre dışı kaldığından, 6. satırda add() metodu tekrar derlendikten sonra addAll() metoduna inline edilmektedir.

JIT tarafından yapılan işlemlerin XML olarak bir dosyaya eklenmesini istiyorsak, JVM i aşağıdaki şekilde çalıştırabiliriz:

[source language=”java”]
c:\>java -server -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation
com.kurumsaljava.jvm.jit.Calculator 100000
[/source]

Oluşan log dosyasını JitWatch uygulaması ile analiz etmek mümkün.

JVM nasıl çalışır yazı serisinin bir sonraki yazısında buluşmak üzere.


EOF (End Of Fun)
Özcan Acar