Using the 20070501 prerelease of gcc 4.2.0, to compile the following sample app: public class Foo { public static void main(String args[]) { System.out.println("Hello."); } } with the following command-line: /usr/local/bin/gcj -v -save-temps -o Foo -static-libgcj --main=Foo Foo.java yeilds a 35MB executable: -rwxr-xr-x 1 aaron aaron 35M 2007-07-03 19:12 Foo* compiler output: Using built-in specs. Reading specs from /usr/local/lib/gcc/i686-pc-linux-gnu/4.2.0/../../../libgcj.spec rename spec startfile to startfileorig rename spec lib to liborig Target: i686-pc-linux-gnu Configured with: ./configure Thread model: posix gcc version 4.2.0 20070501 (prerelease) /usr/local/libexec/gcc/i686-pc-linux-gnu/4.2.0/jc1 Foo.java -fhash-synchronization -fno-use-divide-subroutine -fuse-boehm-gc -fnon-call-exceptions -fkeep-inline-functions -quiet -dumpbase Foo.java -mtune=generic -auxbase Foo -g1 -version -o Foo.s GNU Java version 4.2.0 20070501 (prerelease) (i686-pc-linux-gnu) compiled by GNU C version 4.2.0 20070501 (prerelease). GGC heuristics: --param ggc-min-expand=64 --param ggc-min-heapsize=64499 Class path starts here: ./ /usr/local/share/java/libgcj-4.2.0.jar/ (system) (zip) as --traditional-format -V -Qy -o Foo.o Foo.s GNU assembler version 2.17.50 (i486-linux-gnu) using BFD version 2.17.50 20070103 Ubuntu /usr/local/libexec/gcc/i686-pc-linux-gnu/4.2.0/jvgenmain Foomain Foomain.i /usr/local/libexec/gcc/i686-pc-linux-gnu/4.2.0/cc1 Foomain.i -quiet -dumpbase Foomain.c -mtune=generic -g1 -version -fdollars-in-identifiers -o Foomain.s GNU C version 4.2.0 20070501 (prerelease) (i686-pc-linux-gnu) compiled by GNU C version 4.2.0 20070501 (prerelease). GGC heuristics: --param ggc-min-expand=64 --param ggc-min-heapsize=64499 Compiler executable checksum: 14e4aa58d7d80bfc23a75bbd5755fd63 as --traditional-format -V -Qy -o Foomain.o Foomain.s GNU assembler version 2.17.50 (i486-linux-gnu) using BFD version 2.17.50 20070103 Ubuntu /usr/local/libexec/gcc/i686-pc-linux-gnu/4.2.0/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o Foo /usr/lib/crt1.o /usr/lib/crti.o /usr/local/lib/gcc/i686-pc-linux-gnu/4.2.0/crtbegin.o -L/usr/local/lib/gcc/i686-pc-linux-gnu/4.2.0 -L/usr/local/lib/gcc/i686-pc-linux-gnu/4.2.0/../../.. Foomain.o Foo.o -lgcc_s -lgcc -non_shared -lgcj -call_shared -lm -lpthread -lrt -ldl -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/local/lib/gcc/i686-pc-linux-gnu/4.2.0/crtend.o /usr/lib/crtn.o ============== It appears as if the linker isn't removing uncalled functions from the resulting binary. I will try to build this the old way as well, to see if I get the same issue there.
Same behavior with the "old style" method: > gcj -c Foo.java > gcj --main=Foo -save-temps Foo.java > gcc -o Foo2 Foo.o Foomain.i -shared-libgcc -Wl,-non_shared -lgcj -Wl,-call_shared -lsupc++ -Wl,--as-needed -lz -lgcc_s -lpthread -lc -lm -ldl -lrt -Wl,--no-as-needed Also yields a 35MB binary. Stripping debug symbols drops it to 18MB, full stripping drops it to 11MB.
And this is not a bug, the library just got bigger. You know that your 95 byte source pulls in all of locale, all of thread, most of the java.lang classes in fact.
This is an old discussion and comes up in the mailinglist regularly. The most promising approach is to explicitly exclude parts of the class library. JNC (http://jnc.mtsystems.ch/) supports excluding the GUI stuff (AWT/Swing) and JCE what already leads to a great size reduction (since these will be pulled into every binary). BTW, here the FAQ entry for your question from the JNC website: Why are the binaries so big? As JNC evolves, it supports more and more classes of the Java API. The problem now is, that the Java API classes are heavily interconnected; a simple "Hello World" application uses the security manager which uses AWT which uses... Because of this, you will always have a big part of the classlibrary in your binaries. For Java, this is not a problem. Endusers require to have the JVM, thus having all classes anyway. For native compilation, this is a bigger problem because it's not just a bug that can be fixed. The design of Java doesn't fit into the concept of native compilation (concerning the size of binaries). Since JNC 0.9, you can exclude parts of the class library if you don't need them and thus drastically reduce the size of your binaries.
Marco, thanks for the pointer to JNC. I'm targeting an embedded platform, with only 32MB of storage, so reducing the classes needed is mandatory. From the size of the executable, it looked like the linker had just failed to prune uncalled code. (since libgcj is ~30MB on it's own). Is there a way to pull a report from the linker to determine what methods/classes are left behind and which are included? I did a check with strings and saw all sorts of classes that I'm sure I'm not using (java.util.zip.ZipInputStream for instance).
With "-Wl,-Map,/tmp/TheLinkMap.txt" you get a list of the objects that have been included from libgcj.a.