Andrea aime | 7 Jan 14:15

Executable size problem

Hi,
as you may know, I'm experimenting with gcj as a way to avoid
shipping the jre with the application.

So far I've been relatively successful (and many thanks to the
people that helped me get this far).

Now, the main problem so far is the size of the generated executables.
A "hello world" style app is 8MB (or 5, is you strip it).
I've seen that micro-libgcj can be used to generate small executables,
but most of the platform I rely on usually is stripped out (I develop
Swing apps, web apps, Eclipse RCP apps and the like usually).

I'm wondering, why every class of the standard runtime has to
be compiled into the simple hello world style app? I understand dynamic
class loading is a problem, but one should be able to provide a list
of packages/classes that are dynamically loaded without much problems
(either you know because you have written the code, or you can
get a list by running the application in testing/interactive mode
and see what classes get loaded).
This should allow to compute and strip every uneeded class from the 
executable.

Another approach could be to generate lots of dynamic libraries from
the standard runtime, using packages as a guidance (so, an AWT library,
a Swing library, a XML library, and so on). This way dll could be loaded
at runtime when needed, no? The startup time for a 30MB executable (the 
one I generated from a sample JFace application) is quite noticeable
(at least on Windows XP, I still haven't tried on Linux),
trying to compress it with upx just make things worse memory wise 
(Continue reading)

Andrew Haley | 7 Jan 15:31
Favicon

Re: Executable size problem

Andrea aime writes:
 > 
 > I'm wondering, why every class of the standard runtime has to
 > be compiled into the simple hello world style app?

It isn't.  If you use -Wl,-Map,map while linking you'll get a map file
that shows you what classes are required and why they are required.
It starts like this and goes on for a very long time:

Archive member included because of file (symbol)

/usr/local/lib/libgcj.a(prims.o)
                              /tmp/ccmjvhy6.o (_Jv_AllocObjectNoFinalizer)
/usr/local/lib/libgcj.a(jni.o)
                              /usr/local/lib/libgcj.a(prims.o) (_Jv_JNI_Init())
/usr/local/lib/libgcj.a(exception.o)
                              /tmp/ccmjvhy6.o (__gcj_personality_v0)
/usr/local/lib/libgcj.a(link.o)
                              /usr/local/lib/libgcj.a(jni.o) (_Jv_Linker::resolve_field(_Jv_Field*, java::lang::ClassLoader*))
/usr/local/lib/libgcj.a(defineclass.o)
                              /usr/local/lib/libgcj.a(prims.o) (_Jv_ClassNameSamePackage(_Jv_Utf8Const*, _Jv_Utf8Const*))
/usr/local/lib/libgcj.a(interpret.o)
                              /usr/local/lib/libgcj.a(defineclass.o) (_Jv_soleInterpreterEngine)
/usr/local/lib/libgcj.a(verify.o)
                              /usr/local/lib/libgcj.a(interpret.o) (_Jv_VerifyMethod(_Jv_InterpMethod*))
/usr/local/lib/libgcj.a(Class.o)
                              /usr/local/lib/libgcj.a(prims.o) (java::lang::Class::class$)
/usr/local/lib/libgcj.a(Object.o)
                              /tmp/ccmjvhy6.o (java::lang::Object::class$)

(Continue reading)

Andrea aime | 7 Jan 15:45

Re: Executable size problem

Andrew Haley wrote:
> Andrea aime writes:
>  > 
>  > I'm wondering, why every class of the standard runtime has to
>  > be compiled into the simple hello world style app?
> 
> It isn't.  If you use -Wl,-Map,map while linking you'll get a map file
> that shows you what classes are required and why they are required.
> It starts like this and goes on for a very long time:
> 
> 
> Archive member included because of file (symbol)
> 
> /usr/local/lib/libgcj.a(prims.o)
>                               /tmp/ccmjvhy6.o (_Jv_AllocObjectNoFinalizer)
> /usr/local/lib/libgcj.a(jni.o)
>                               /usr/local/lib/libgcj.a(prims.o) (_Jv_JNI_Init())

I'll try... I've compiled the same hello world style app with gcj 3.4
(from thisiscool.com) and the executable is much smaller (around 2 MB
if my memory helps me). Has the set of required classes grown so much
during the 3.4/4.0 timeframe?

> Not entirely, but the problems associated with static linkage aren't
> only related to dynamic loading.  For example, library bug fixes
> become quite ineffective with static linkage.  Also, you don't get the
> benefit of sharing the same libraries between different processes.  I
> understand dynamic linkage isn't popular on Windows for a variety of
> reasons, but I'm certain that it's a bad idea on GNU/Linux.

(Continue reading)

Andrea aime | 7 Jan 17:07

Re: Executable size problem

Andrew Haley wrote:
> Andrea aime writes:
>  > 
>  > I'm wondering, why every class of the standard runtime has to
>  > be compiled into the simple hello world style app?
> 

I have done a few tests and managed to get the executable size down 
quite a bit.
Instead of linking all the libraries with all their contents, I used
ProGuard (a shrinker/obfuscator) to preprocess org.eclipse.* jars
in order to keep only the classes needed by the application, and then
compiled again with gcj. Surprise surprise, the executable is now "only"
18MB instead of 30MB, and it works, too: the linker did not shave off
many uneeded org.eclipse.* classes when I linked the original jars.
(if you want the proguard configuration file, I can send it to you).

I'm wondering if I can shrink it further by letting ProGuard process the
gjc runtime libraries... is it possible to get the runtime library as a
jar file and make the linker avoid linking libgcj.a? Just curious.

Best regards
Andrea Aime

Ranjit Mathew | 7 Jan 16:19

Re: Executable size problem


Andrea aime wrote:
> 
> I'm wondering, why every class of the standard runtime has to
> be compiled into the simple hello world style app? I understand dynamic

The Java runtime library is unfortunately rather heavily
inter-linked. Even a simple "Hello World" class loads in
an additional 293(!) classes with Sun's JDK 1.5.0-05 on
Linux:

  ~/src/tmp > java -verbose:class Hello |grep Loaded |wc -l
      294

I'm not saying that GCJ is blame-free here, but you
should keep the above in mind when comparing the sizes
of "Hello World" applications across, say, C, C++ and Java.

I really wish they had given some more thought to
library design and adopted a layered approach (beyond
J2SE and J2EE). No wonder the J2ME guys had to come
up with so many kludges. Rather ironic considering
that Java was supposedly meant for embedded devices
when they started the project. :-/

Ranjit.

--
Ranjit Mathew       Email: rmathew AT gmail DOT com

(Continue reading)

Anthony Green | 7 Jan 18:01
Favicon

Re: Executable size problem

On Sat, 2006-01-07 at 14:18 +0100, Andrea aime wrote:
> Now, the main problem so far is the size of the generated executables.
> A "hello world" style app is 8MB (or 5, is you strip it).
> I've seen that micro-libgcj can be used to generate small executables,
> but most of the platform I rely on usually is stripped out (I develop
> Swing apps, web apps, Eclipse RCP apps and the like usually).

The most sophisticated tool I remember for reducing static binary size
was from Adam Megacz.  I'm pretty sure he released the code for it but I
couldn't find it (after only a single google attempt).

Here's a reference to it:
http://gcc.gnu.org/ml/java/2002-02/msg00202.html

It sounds wild:
  "Yeah, it's actually a Java program that does reachability analysis on
  the bytecode version of your program, and then spews out a linker
  script that will drop the unreachable methods. It also has some hacks
  to know what methods inside libgcj are invoked by native methods. I
  got HelloWorld down to 133kb gzipped."

If you search and find any more references to this (or contact Adam),
post your findings to this list.

Thanks,

AG


Gmane