Java Sanal Makinesi - JIT Derleyicisi

Bu bölümde, JIT derleyicisini ve derlenmiş ve yorumlanmış diller arasındaki farkı öğreneceğiz.

Derlenmiş ve Yorumlanmış Diller

C, C ++ ve FORTRAN gibi diller derlenmiş dillerdir. Kodları, temel makineye hedeflenen ikili kod olarak teslim edilir. Bu, yüksek seviyeli kodun, temeldeki mimari için özel olarak yazılmış statik bir derleyici tarafından bir kerede ikili koda derlendiği anlamına gelir. Üretilen ikili, başka herhangi bir mimaride çalışmayacaktır.

Öte yandan, Python ve Perl gibi yorumlanan diller, geçerli bir tercümana sahip oldukları sürece herhangi bir makinede çalışabilir. Üst düzey kod üzerinden satır satır üzerinden geçerek bunu ikili koda dönüştürür.

Yorumlanan kod genellikle derlenmiş koddan daha yavaştır. Örneğin, bir döngü düşünün. Yorumlanan, döngünün her yinelemesi için karşılık gelen kodu dönüştürür. Öte yandan, derlenmiş bir kod, çeviriyi yalnızca bir tane yapacaktır. Ayrıca, yorumlayıcılar bir seferde yalnızca bir satır gördüklerinden, derleyiciler gibi ifadelerin yürütme sırasını değiştirmek gibi önemli bir kod gerçekleştiremezler.

Aşağıda bu tür bir optimizasyon örneğini inceleyeceğiz -

Adding two numbers stored in memory. Belleğe erişim birden fazla CPU döngüsünü tüketebileceğinden, iyi bir derleyici verileri bellekten almak için talimatlar yayınlayacak ve eklemeyi yalnızca veri mevcut olduğunda gerçekleştirecektir. Beklemeyecek ve bu arada diğer talimatları uygulayacaktır. Öte yandan, yorumlayıcı herhangi bir zamanda tüm kodun farkında olmadığından, yorumlama sırasında böyle bir optimizasyon mümkün olmayacaktır.

Ancak, yorumlanmış diller, o dilin geçerli bir yorumlayıcısına sahip herhangi bir makinede çalışabilir.

Java Derlenmiş mi Yorumlanmış mı?

Java bir orta yol bulmaya çalıştı. JVM, javac derleyicisi ile temeldeki donanım arasında oturduğundan, javac (veya başka herhangi bir derleyici) derleyici, platforma özel bir JVM tarafından anlaşılan Bytecode'da Java kodunu derler. Daha sonra JVM, kod yürütülürken JIT (Just-in-time) derlemesini kullanarak Bytecode'u ikili olarak derler.

HotSpot'lar

Tipik bir programda, sıkça yürütülen yalnızca küçük bir kod bölümü vardır ve çoğu zaman, tüm uygulamanın performansını önemli ölçüde etkileyen bu koddur. Bu tür kod bölümleri denirHotSpots.

Kodun bir bölümü yalnızca bir kez çalıştırılırsa, bunu derlemek çaba israfı olur ve bunun yerine Bytecode'u yorumlamak daha hızlı olur. Ancak bölüm sıcak bir bölümse ve birden çok kez yürütülürse, JVM bunun yerine onu derler. Örneğin, bir yöntem birden çok kez çağrılırsa, kodu derlemek için gereken fazladan döngüler, üretilen daha hızlı ikili dosya tarafından dengelenecektir.

Dahası, JVM belirli bir yöntemi veya bir döngüyü ne kadar çok çalıştırırsa, daha hızlı bir ikili dosya oluşturmak için çeşitli optimizasyonları yapmak için o kadar fazla bilgi toplar.

Şu kodu ele alalım -

for(int i = 0 ; I <= 100; i++) {
   System.out.println(obj1.equals(obj2)); //two objects
}

Bu kod yorumlanırsa, yorumlayıcı her yineleme için obj1 sınıflarının çıkarsaması yapacaktır. Bunun nedeni, Java'daki her sınıfın Object sınıfından genişletilen ve geçersiz kılınabilen bir .equals () yöntemine sahip olmasıdır. Yani obj1 her yineleme için bir dizge olsa bile, kesinti yine de yapılacaktır.

Öte yandan, gerçekte olan şey, JVM'nin her yineleme için obj1'in String sınıfından olduğunu fark etmesi ve dolayısıyla doğrudan String sınıfının .equals () yöntemine karşılık gelen kodu üretmesidir. Böylece hiçbir arama gerekmeyecek ve derlenen kod daha hızlı çalışacaktır.

Bu tür bir davranış ancak JVM kodun nasıl davrandığını bildiğinde mümkündür. Böylece kodun belirli bölümlerini derlemeden önce bekler.

Aşağıda başka bir örnek var -

int sum = 7;
for(int i = 0 ; i <= 100; i++) {
   sum += i;
}

Her döngü için bir yorumlayıcı hafızadan 'toplam' değerini alır, ona 'I' ekler ve hafızaya geri depolar. Bellek erişimi pahalı bir işlemdir ve genellikle birden çok CPU döngüsü gerektirir. Bu kod birden çok kez çalıştığı için bir HotSpot'tur. JIT bu kodu derleyecek ve aşağıdaki optimizasyonu yapacaktır.

'Toplam'ın yerel bir kopyası, belirli bir iş parçacığına özel bir kayıtta saklanacaktır. Tüm işlemler yazmaçtaki değere yapılır ve döngü tamamlandığında değer belleğe geri yazılır.

Ya başka iş parçacıkları da değişkene erişiyorsa? Güncellemeler başka bir iş parçacığı tarafından değişkenin yerel bir kopyasına yapıldığından, eski bir değer göreceklerdir. Bu gibi durumlarda iş parçacığı senkronizasyonu gereklidir. Çok temel bir ilkel senkronizasyon, 'toplamı' değişken olarak ilan etmek olacaktır. Şimdi, bir değişkene erişmeden önce, bir iş parçacığı yerel kayıtlarını temizler ve değeri bellekten alır. Erişim sağlandıktan sonra, değer hemen belleğe yazılır.

Aşağıda, JIT derleyicileri tarafından yapılan bazı genel optimizasyonlar bulunmaktadır -

  • Satır içi yöntem
  • Ölü kod eleme
  • Arama sitelerini optimize etmek için buluşsal yöntemler
  • Sabit katlama