2010-03-11

Compiling Java applications to native Windows executables

Being a Java developer really sucks when it comes to making end-user desktop applications. You want these applications to be light, fast and easily redistributable. Packaging a bunch of jars along with startup script is the worst thing you can do. It may work quite well in various Linux distributions, where you can make a .deb or .rpm which automatically installs JRE, puts startup script to /usr/local/bin and creates a nice launcher with icon in the applications menu. In Mac OS you can use that outdated JRE that comes by default and then easily bundle all scripts and jars into an .app. But in Windows it does not work that way. You can use JSmooth to make .exe out of .jar and use NSIS to create a nice installer, but there still is a problem. User may not have a compatible JRE, or he/she may choose not to have one just because Java sucks for running on it's bloated memory hogging virtual machine. I have to agree here. As a user, I have no faith in desktop applications that are written in Java.

So what is the solution? Compile to native executable and forget JRE. It is possible, but it will not be a walk in the park. First, you will have to get familiar with GNU Compiler Collection and particularly with GCJ.

Let's compile a Hello World application like this one:

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello native world!");
    }
}
  1. Get the patched GCJ here: http://www.thisiscool.com/gcc_mingw.htm (120 MB)
  2. Extract it somewhere and add thisiscool-gcc/gcc-ejc/bin to your PATH
  3. Compile Hello.java as Hello.exe: gcj --main=Hello -o Hello Hello.java
  4. Enjoy your statically linked Hello.exe which prints Hello native world! and runs without JRE.
Wait. Hello.exe is 37MB? Holy crap! Well, it's a little overhead you have to pay for loosing the JRE, this .exe contains the whole garbage collection mechanism, etc. Also, you will be able to compile SWT GUI applications! It is possible to get smaller executables with MinGW tooling. Hello.java compiled with MinGW is about 3MB, but it has other issues, I couldn't manage to get the system output working. You may be more lucky though.

Anyway, when you compile native binaries, your Java code cannot be decompiled, so you have better protection than by using obfuscators. And the best thing is that end users won't complain that "it's crap because it's Java". They just wouldn't know.

I wish Sun (I just can't say Oracle when referring to Java, it makes me sick, sorry) could create an official AOT Java compiler so developers would not have to go through hellfire to make native executables.

9 comments:

  1. Have you tried Excelsior JET? Its Java Runtime Slim-Down can yield a less-than-10MB installer for a Java GUI app, even Swing. It may also compile Eclipse RCP and Tomcat Web apps.

    ReplyDelete
  2. I didn't mention Excelsor JET because it's shareware and a really expensive one. That's also the reason why I didn't try it.

    ReplyDelete
  3. A non-commercial project may qualify for a free Excelsior JET license.

    As for GCJ, apparently you get what you pay for. Excelsior JET is much easier to use and is Java SE 6 compatible. In particular, it includes the reference implementation of the standard Java API, licensed from Sun.

    ReplyDelete
  4. "You want these applications to be light, fast and easily redistributable" - I think Java WEB start is suitable for these requirements.
    It requires JRE installed, there is special web site for this purpose - http://www.java.com/en/download/help/testvm.xml

    ReplyDelete
  5. @dleskov
    I have a feeling that you work for Excelsior. :)

    @Vic
    Java Web Start might be good for redistributing small things like casual games, but for other things it is even worse. You depend not only on JRE, but also on internet connection. For me a significant part of "easily redistributable" is the fact that you can burn your software to CD and install where internet connection is not available.

    ReplyDelete
  6. @Tomas Varaneckas: Yes I do, sorry for not making an explicit disclosure.

    ReplyDelete
  7. GCJ compiled code is slower than code run on top the the JVM since Java 1.3 was released with HotSpot. Dynamic compiling kills the static optimizations, which are the only kind of optimizations gcj can do. And as nobody uses GCJ, I bet it barely optimizes anything.
    Making an executable jar is easy. Just run a Maven command

    mvn assembly:assembly

    And the jar you get runs by double clicking on it on windows. Installing the JRE there days is as easy as installing flash. Even big companies allow JRE in employee desktops as they know it is useful.

    ReplyDelete
  8. There is no comparison between GCJ compiled java and Excelsior Jet compiled java. The former is not even usable in my opinion as 9 times out of 10 it fails to compile for any reasonably complex GUI app. Excellsior Jet almost never fails and produces a way better executable that actually runs 99.9 faster times out of 100. GCJ is for toy apps only as far as I can tell. Most of the time when I finally do get the darn thing to compile it then fails to launch. Maybe I don't know how to use it but that has been my experience. Excelsior Jet is trivial to use and also creats a nice installation package. And no I do not work for Excelsior Jet. I have used both.

    ReplyDelete
  9. @ignorante

    Static optimizations, if implemented properly, will kill "dynamic" JIT - just because it runs offline and has time to analyze the program more thoroughly. Moreover, JIT splatters native code over the memory, which is really unfriendly to cache. The only thing where JIT may have advantage is detecting hot/cold code, but static optimization could also account for that (as they do in C++ world with profile-guided optimization).

    All in all, JIT is a conceptual hack, something you do when you don't have the time to compile properly.

    ReplyDelete

Spam comments (i.e. ones that contain links to web development services) will be reported along with user profiles!

Note: only a member of this blog may post a comment.