Java sınıfları javac derleyicisi ile derlenir ve sekizlik kodlar (=bytecode) içeren class uzantılı ikili bir dosya üretilir:
[centos@guru src]$javac -d ../bin com/example/animals/application/*.java
Bir uygulama çok sayıda Java sınıfından oluşur. Bu nedenle uygulamanın dağıtımını yapmak için tek bir dağıtım dosyasına dönüştürmek gerekir. Java Standard Edition'da dağıtım dosyası jar'dır. Bir proje için jar dağıtım dosyasını üretmek için aynı isimli jar uygulamasını kullanıyoruz:
[centos@guru bin]$ jar -ecvf com.example.animals.application.AnimalApp app.jar . added manifest adding: com/(in = 0) (out= 0)(stored 0%) adding: com/example/(in = 0) (out= 0)(stored 0%) adding: com/example/animals/(in = 0) (out= 0)(stored 0%) adding: com/example/animals/application/(in = 0) (out= 0)(stored 0%) adding: com/example/animals/application/AnimalAppInJava7.class(in = 1322) (out= 788)(deflated 40%) adding: com/example/animals/application/AnimalApp.class(in = 4253) (out= 1802)(deflated 57%) adding: com/example/animals/domain/(in = 0) (out= 0)(stored 0%) adding: com/example/animals/domain/Animal.class(in = 554) (out= 382)(deflated 31%) adding: com/example/animals/domain/Cat.class(in = 1464) (out= 730)(deflated 50%) adding: com/example/animals/domain/Pet.class(in = 218) (out= 167)(deflated 23%) adding: com/example/animals/domain/Fish.class(in = 1565) (out= 762)(deflated 51%) adding: com/example/animals/domain/Spider.class(in = 1007) (out= 554)(deflated 44%)
Java uygulamasını çalıştırmak için ise Java Sanal Makinasına ihtiyaç bulunuyor. java komutu ile Java Sanal Makinasına yazılımsal olarak erişmiş oluyoruz. jar olarak dağıtılmış bir Java uygulamasını basitçe aşağıdaki gibi bir komutla çalıştırabiliriz:
[centos@guru bin]$ java -cp app.jar com.example.animals.application.AnimalApp
Eğer jar oluşturulurken çalıştırılacak sınıfın hangi sınıf olduğu, başka bir deyişle uygulamanın giriş noktası tanımlanmışsa, uygulamayı daha da basit bir şekilde aşağıda verildiği gibi çalıştırabiliriz:
[centos@guru bin]$ java -jar app.jar
jar dağıtım dosyası üretilirken, jar komutuna uygulamanın giriş noktasının (çalıştırılacak sınıfın) hangisi olduğunu -e seçeneği ile veriyoruz.
JIT derleyici sekizlik kodları JSM’nin üzerinde çalıştığı platformun anlayacağı komutlara dönüştürür. Üstelik bunu yaparken devingen en iyileme de yapar. Bunun için uygulamanın basit bir kesitini (=profiling) çıkarır. Bu kesit bilgisine göre onlarca en iyileme tekniğinden hangilerini uygulayacağına karar verir. JIT'lenen kodlar elbette hızlı çalışır. Ancak bu işlemin bir zaman maliyeti vardır ve bu ödenen bedel özellikle uygulamanın açılış süresinin biraz uzamasına ve uygulamada yer yer gecikmelerin yaşanmasına neden olur. JIT derleyici sınıf metotlarını, döngü bloklarını ve hataları ele aldığımız catch bloklarını derler.
Java Sanal Makinasının her türlü davranışını görünür kılabiliriz. Buna JIT derleyicinin davranışı da dahildir. JIT'lenen metotları görmek için uygulamayı aşağıdaki parametrelerle çalıştırmalısınız:
[centos@guru bin]$ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -jar app.jar
Hatta JIT'lenen metotlarını makina dili karşılıklarını bile görebilirsiniz:
[centos@guru bin]$ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -jar app.jar
Ancak yukarıdaki komutun ekrana JIT derleyicinin ürettiği kodun makina dili karşılığını yazabilmesi için özel bir kütüphaneye ihtiyacınız bulunuyor.
Oracle firmasının sağladığı Java Sanal Makina ürününün adı HotSpot'dur. Hotspot içinde iki farklı karakteristikte JIT derleyici bulunur: C1 ve C2. Java uygulamalarının başarımını ölçmek için farklı metrikler kullanarak ölçümler yapabiliriz. Açılış zamanı ve cevap süresi başarımı ölçmek için kullanılabilecek iki önemli ölçüttür. Masaüstü uygulamaları için açılış süresi ve web uygulamaları için ise cevap süresi daha önemlidir. Masaüstü uygulamaları, kullanıcı arayüzünü bir an önce kullanıcıya sunmalıdır. Örneğin, e-posta göndermek istiyorsunuz, bunun için favori e-posta istemcinizin masaüstü simgesine tıkladınız. Pencerenin hemen açılmasını ve iletinizi bir an önce yazabilmeyi istersiniz. Nasıl olsa yazmaya başladığınızda, işlemci ile karşılaştırıldığında kaplumbağa hızında yazıyor olacaksınız. Yazılımın çok da hızlı çalışmaya ihtiyacı yok. Buna karşılık web uygulamalarında ise uygulama sunucusunun biraz geç açılmasına tahammül edebilirsiniz ama bir istek geldiğinde ona en hızlı sürede yanıt vermek ve cevabı dönebilmek istersiniz. İşte, C1 derleyicisi açılış süresini iyileştirmek ve C2 derleyicisi ise hızlı yanıt süresini iyileştirmek üzere en iyilenmiştir.
Ancak biz uygulamanın hem hızlı açılmasını hem de kısa sürede çok iş çıkarmasını isteriz! Java 7'de her iki JIT derleyiciyi (C1 ve C2) de katmanlı bir yapıda kullanan bir derleyici geldi: Katmanlı JIT Derleyici (=Tiered Compiler). Java 7'de bu derleyici varsayılan olarak kapalı geliyor. Açmak için uygulamayı -XX:+TieredCompilation seçeneği açık olacak şekilde çalıştırmanız gerekiyor:
[centos@guru bin]$ java -XX:+TieredCompilation -jar app.jar
Java 8'de ise katmanlı derleyici olgunlaştığı için varsayılan özellik olarak açık geliyor! Herhangi bir seçenek vermenize gerek bulunmuyor!
Peki, neden uygulama çalıştırılmadan önce class uzantılı dosyaları doğrudan uygulamanın üzerinde çalışacağı işlemcinin anlayacağı komutlara dönüştürmüyoruz? Genel olarak bu işlem uygulamanın birim zamanda iş çıkarma kapasitesini düşürecektir. Ancak bazı özel durumlarda uygulamanın açılış süresini iyileştirme potansiyeli bulunuyor. Java 9'da çalışma zamanından önce derleme (=ahead of time compilation, AOT) geldi. Başlangıçta, bu özelliğin sadece 64-bitlik Linux sistemlerde çalıştığını belirtmem gerekiyor.
Çalışma zamanından önce derleyici Java Geliştirme Kiti içinden çıkan bir araç olarak geliyor: jaotc. jaotc ile bir sınıfı, bir dağıtım dosyası içindeki sınıfları ve bir modül içindeki sınıfları derleyebiliriz. Derlendiğinde ise so uzantılı paylaşımlı kütüphane dosyası oluşturuluyor.
Önce bir sınıfı jaotc ile derleyelim:
[centos@guru bin]$ jaotc --compile-for-tiered -J-XX:-UseAOT -J-Xmx2g --info com/example/animals/application/AnimalApp.class --output ./libanimalapp.so Compiling ./libanimalapp... 1 classes found (59 ms) 4 methods total, 4 methods to compile (7 ms) Compiling with 2 threads . 4 methods compiled, 0 methods failed (852 ms) Parsing compiled code (4 ms) Processing metadata (28 ms) Preparing stubs binary (1 ms) Preparing compiled binary (0 ms) Creating binary: ./libanimalapp.o (15 ms) Creating shared library: ./libanimalapp.so (30 ms) Total time: 1863 ms
Şimdi bir jar dağıtım dosyası içindeki sınıfları jaotc ile derleyelim:
[centos@guru bin]$ jaotc --compile-for-tiered -J-XX:-UseAOT -J-Xmx2g --info --jar app.jar --output ./libanimals.so Compiling ./libanimals... 7 classes found (97 ms) 31 methods total, 27 methods to compile (11 ms) Compiling with 2 threads . 27 methods compiled, 0 methods failed (2214 ms) Parsing compiled code (4 ms) Processing metadata (54 ms) Preparing stubs binary (1 ms) Preparing compiled binary (1 ms) Creating binary: ./libanimals.o (20 ms) Creating shared library: ./libanimals.so (20 ms) Total time: 3211 ms
Son olarak ise java.sql modülü içindeki sınıfları jaotc ile derleyelim:
[centos@guru ~]$ jaotc --compile-for-tiered -J-XX:-UseAOT -J-Xmx2g --info --module java.sql --output ./libjava.sql-tiered.so Compiling ./libjava.sql-tiered... 81 classes found (113 ms) 1311 methods total, 323 methods to compile (101 ms) Compiling with 2 threads .... 323 methods compiled, 0 methods failed (6650 ms) Parsing compiled code (26 ms) Processing metadata (226 ms) Preparing stubs binary (1 ms) Preparing compiled binary (3 ms) Creating binary: ./libjava.sql-tiered.o (131 ms) Creating shared library: ./libjava.sql-tiered.so (208 ms) Total time: 8365 ms
Uygulamaları çalıştırırken Java Sanal Makinasının, so kütüphanesinde verilen metodları kullanmasını ise aşağıdaki örneklerdeki gösterilen seçenekleri vererek sağlıyoruz:
[centos@guru bin]$ java -XX:AOTLibrary=./libanimalapp.so -XX:+UnlockDiagnosticVMOptions -XX:+PrintAOT -XX:+UseAOTStrictLoading -XX:+UseAOT -jar app.jar com.example.animals.application.AnimalApp 15 1 loaded ./libanimalapp.so aot library 172 1 aot[ 1] com.example.animals.application.AnimalApp.main([Ljava/lang/String;)V 172 2 aot[ 1] com.example.animals.application.AnimalApp.<init>()V 172 3 aot[ 1] com.example.animals.application.AnimalApp.lambda$main$1(Lcom/example/animals/domain/Animal;)Z 172 4 aot[ 1] com.example.animals.application.AnimalApp.lambda$main$0(Ljava/lang/Class;Lcom/example/animals/domain/Animal;)V
[centos@guru bin]$ java -XX:AOTLibrary=./libanimals.so -XX:+UnlockDiagnosticVMOptions -XX:+PrintAOT -XX:+UseAOTStrictLoading -XX:+UseAOT -jar app.jar com.example.animals.application.AnimalApp 11 1 loaded ./libanimals.so aot library 144 1 aot[ 1] com.example.animals.application.AnimalApp.<init>()V 144 2 aot[ 1] com.example.animals.application.AnimalApp.lambda$main$1(Lcom/example/animals/domain/Animal;)Z 144 3 aot[ 1] com.example.animals.application.AnimalApp.lambda$main$0(Ljava/lang/Class;Lcom/example/animals/domain/Animal;)V 144 4 aot[ 1] com.example.animals.application.AnimalApp.main([Ljava/lang/String;)V 181 5 aot[ 1] com.example.animals.domain.Animal.getLegs()I 181 6 aot[ 1] com.example.animals.domain.Animal.<init>(I)V 181 7 aot[ 1] com.example.animals.domain.Animal.walk()V 181 8 aot[ 1] com.example.animals.domain.Cat.<init>(Ljava/lang/String;)V 181 9 aot[ 1] com.example.animals.domain.Cat.<init>()V 181 10 aot[ 1] com.example.animals.domain.Cat.play()V 181 11 aot[ 1] com.example.animals.domain.Cat.eat()V 181 12 aot[ 1] com.example.animals.domain.Cat.toString()Ljava/lang/String; 181 13 aot[ 1] com.example.animals.domain.Cat.getName()Ljava/lang/String; 181 14 aot[ 1] com.example.animals.domain.Cat.setName(Ljava/lang/String;)V 181 15 aot[ 1] com.example.animals.domain.Spider.<init>()V 181 16 aot[ 1] com.example.animals.domain.Spider.eat()V 181 17 aot[ 1] com.example.animals.domain.Spider.toString()Ljava/lang/String; 181 18 aot[ 1] com.example.animals.domain.Fish.<init>(Ljava/lang/String;)V 181 19 aot[ 1] com.example.animals.domain.Fish.<init>()V 181 20 aot[ 1] com.example.animals.domain.Fish.play()V 181 21 aot[ 1] com.example.animals.domain.Fish.toString()Ljava/lang/String; 181 22 aot[ 1] com.example.animals.domain.Fish.eat()V 181 23 aot[ 1] com.example.animals.domain.Fish.getName()Ljava/lang/String; 181 24 aot[ 1] com.example.animals.domain.Fish.setName(Ljava/lang/String;)V 181 25 aot[ 1] com.example.animals.domain.Fish.walk()V
jaotc derlenen ve çalıştırılan kod parçaları başlangıçta yorumlamalı çalıştırmaya göre kesinlikle daha hızlı çalışacaktır. Üstelik başlangıç bu kodun JIT'lenmesi için zaman kaybedilmediği için uygulamamız potansiyel olarak daha hızlı açılacak ve hızlı çalışacaktır. AOT kullanmadan önceki ve sonraki başarımı mutlaka ölçmelisiniz!
No comments:
Post a Comment