Dan Bornstein had a correction about my guesswork on how dx works, and I got permission to post it here. I said:
The end result is a little wasteful: even a tiny “hello world” app is 800K, probably because of that big ol' scala library. I suspect dex is proactively throwing away classes that I didn’t use, or it would be even bigger.
Actually, dx doesn’t do any sort of tree-shaking. It’s just that I obsessed over making the dex format compact, and I got a bit of help on that front from mikef who designed a super-tight encoding for debug info (line numbers, local variables, etc.).
An uncompressed dex file is usually a little smaller (5% or so) than a corresponding (compressed) jar file, and for distribution a compressed dex file is generally about 40-45% the size of that.
For comparison on the distribution front, a compressed pack200 is still usually a bit better than a compressed dex file, weighing in at 30-35% of the original compressed jar size, a win which it gets by translating class files into a compressability-optimized intermediate format (doing stuff like collating immediate integer constants out of bytecode streams). The distinction here is that the dex format loses a bit of compressability, trading that for suitability to be directly executed.
We decided that the overhead (both in terms of code and runtime) of doing something like pack200 wasn’t worth it for us, at least not yet, because most apps don’t actually have that much code (much more likely to be heavy with media), though if people start regularly including language runtimes with their apps, we may want to revise.
Oh, a much more minor tidbit: You can pass dx options for the underlying JVM by prefixing them with “-J”, e.g. “-JXmx2000m” or what-have-you.