Java: kompilace do nativniho kodu platformy

Programy v Jave, se na rozdil treba od C nekompiluji primo do nativniho kodu dane platformy, ale do bajt kodu (mezikodu). To muze velka (ne)vyhoda. zalezi na uhlu pohledu. Aplikace je pak multiplatformni, jde spustit na vsech OS, na ktere je portovana Java. Nevyhodnou je mozna trochu mensi rychlost (tohle tema nemam rad ;-)) a relativne snadna moznost dekompilovat zkompilovane class soubory. Malo se to vi a jeste min pouziva, ale i Java se do zkompilovat primo do nativniho kodu.

Nechce se mi psat o teorie, te je plny web. Takze kratce: zdrojak v Jave jde opravdu zkompilovat do nativni binarky. Na Windows se to casto resi pribalenim JRE (Java Runtime Environment) k JAR souboru s aplikaci. Cela obludnost ma pak minimalne 100MB. Navic je tam JRE navzdy neaktualizovane.

Pravdepodobne nejlepsi na kompilaci do nativniho kodu je komercni nastroj Excelsior JET, bohuzel jsem ho vsak nevyzkousel.

Otevrenou alternativou (no dobra, alternativa je mozna silne slovo) je GCJ (GNU ClassPath). Projekt je aktualne asi v mrtve fazi a nic se v nem nedeje. Podle oficialniho webu je podpora na urovni „It has been merged with GNU Classpath and supports most of the 1.4 libraries plus some 1.5 additions.“. Takze skoro cela Java 1.4 a cast Javy 1.5.

Jen pro upresneni, Java 1.4 je z roku 2002 a 1.5 (Java 5.0) z roku 2004. Podpora AWT a Swingu je takova…pochybna, skoro zadna…

Dalsi mytus: vysledna binarka bude obludne velka. V pripade GCJ to u „Hello, world“ neplatilo. Zkouset cokoliv dalsiho jsem povazoval za plytvani casem. GCJ jsem zkousel z jedineho duvodu – dokazit si, ze to jde 🙂

Protoze jsem mel u testovani spustenych X dalsich programu, jsou casy pouze orientacni. Je tak videt, ze vysledna binarka i kompilace trva u Oracle Javy i GCJ +- stejne dlouho.

Na uvod pouzite verze Javy. Pracoval jsem na Linux Mint 16 (Ubuntu 13.10):

########### Verze Javy: Oracle Java 8 64bit; gcc (gcj) 4.8.1 64bit

martin@martin:~/gcj/test$ gcj -v
Using built-in specs.
Reading specs from /usr/lib/gcc/x86_64-linux-gnu/4.8/libgcj.spec
rename spec startfile to startfileorig
rename spec lib to liborig
COLLECT_GCC=gcj
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.8.1-10ubuntu9' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu9)
 
martin@martin:~/gcj/test$ java -version
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)
 

Ukazkovy „Hello, world“ program:

martin@martin:~/gcj/test$ cat HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        Pozdrav pozdrav = new Pozdrav("Hello, world!");
        pozdrav.print();
    }
}
martin@martin:~/gcj/test$ cat Pozdrav.java
public class Pozdrav {
    private String text;
 
    public Pozdrav(String pozdrav) {
        text = pozdrav;
    }
    private String getText() {
        return text;
    }
    public void print() {
        System.out.println(getText());
    }
}

Velikosti souboru pred kompilaci:

martin@martin:~/gcj/test$ ls -lh
celkem 8,0K
-rw-r--r-- 1 martin martin 160 říj 13 23:12 HelloWorld.java
-rw-r--r-- 1 martin martin 250 říj 13 23:12 Pozdrav.java

Kompilace pomoci Oracle Javy a GCJ:

martin@martin:~/gcj/test$ time javac HelloWorld.java
real    0m0.793s
user    0m1.528s
sys     0m0.076s
 
martin@martin:~/gcj/test$ time gcj -O2 -g -Wall --main=HelloWorld -o HelloWorldGCJ HelloWorld.java Pozdrav.java
real    0m0.597s
user    0m0.515s
sys     0m0.081s

Velikosti vystupnich souboru:

martin@martin:~/gcj/test$ ls -lh
celkem 40K
-rw-r--r-- 1 martin martin 371 říj 13 23:12 HelloWorld.class
-rwxr-xr-x 1 martin martin 22K říj 13 23:15 HelloWorldGCJ
-rw-r--r-- 1 martin martin 160 říj 13 23:12 HelloWorld.java
-rw-r--r-- 1 martin martin 525 říj 13 23:12 Pozdrav.class
-rw-r--r-- 1 martin martin 250 říj 13 23:12 Pozdrav.java

Velikosti vystupnich souboru po stripu GCJ binarky:

martin@martin:~/gcj/test$ strip HelloWorldGCJ
 
martin@martin:~/gcj/test$ ls -lh
celkem 28K
-rw-r--r-- 1 martin martin 371 říj 13 23:12 HelloWorld.class
-rwxr-xr-x 1 martin martin 12K říj 13 23:15 HelloWorldGCJ
-rw-r--r-- 1 martin martin 160 říj 13 23:12 HelloWorld.java
-rw-r--r-- 1 martin martin 525 říj 13 23:12 Pozdrav.class
-rw-r--r-- 1 martin martin 250 říj 13 23:12 Pozdrav.java

Informace o zkompilovanych souborech:

martin@martin:~/gcj/test$ file HelloWorldGCJ
HelloWorldGCJ: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x8a4bf227c54ab5357c44b2b9e5d2d11fc1e7d1d6, stripped
 
martin@martin:~/gcj/test$ file HelloWorld.class
HelloWorld.class: compiled Java class data, version 52.0
 
martin@martin:~/gcj/test$ file Pozdrav.class
Pozdrav.class: compiled Java class data, version 52.0

Porovnani rychlosti provedeni programu z Oracle Javy a GCJ binarky:

martin@martin:~/gcj/test$ time java HelloWorld
Hello, world!
real    0m0.073s
user    0m0.067s
sys     0m0.019s
 
martin@martin:~/gcj/test$ time ./HelloWorldGCJ
Hello, world!
real    0m0.035s
user    0m0.022s
sys     0m0.013s

Zaver o (ne)pouzitelnosti si kazdy musi udelat sam. Muj test nema prilis velkou vypovidaci hodnotu, na to jsem testoval malo veci. Chtelo by to slozitejsi program. Ale jak rikam, jen jsem si chtel na TODO listu odskrtnout „vytvorit Java ELF binarku“ a to se povedlo.

Mate-li s GCJ nejake blizsi zkusenosti, podelte se prosim v diskuzi pod clankem.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *