
Really Small Java Apps - pcr910303
http://august.nagro.us/small-java.html
======
lvh
GraalVM native-image is not a listed option, but I regularly use it to produce
binaries that are competitive with (often better than) Go in terms of size and
(start time) perf.

A Clojure tool that parses, traverses and processes JSON (caro, see below)
worked out to 3.2MB. I'm sure C and Rust can do better, but it's not bad
compared to a JVM, and it's an order of magnitude better than the best option
in the article. It's also small enough that while comparisons like "that's
three floppies!" are interesting historical perspective, you can't reasonably
complain about having a 3 MB binary in ~/.local/bin.

Examples:

[https://github.com/latacora/wernicke/releases](https://github.com/latacora/wernicke/releases)
[https://github.com/latacora/recidiffist-
cli/releases](https://github.com/latacora/recidiffist-cli/releases)
[https://github.com/latacora/caro/releases](https://github.com/latacora/caro/releases)

It mostly works out of the box. My biggest frustration is that you can't
easily link in dynamic libraries, which makes it a little annoying to do e.g.
EC TLS with a single binary. (Go would have a similar problem but sidestepped
it by reimplementing most of it natively.) Second biggest frustration: it is
not a fast compiler. You're definitely doing development on the JVM (I use
Graal as my default JVM now) and a "production build" afterwards with native-
image.

~~~
vips7L
>I regularly use it to produce binaries that are competitive with (often
better than) Go in terms of size and (start time) perf.

How do they compare to go in terms of speed? Another comment also said graal
native images still suffer with throughput vs using the jvm due to profile
guided optimizations.

~~~
chrisseaton
It's still optimised native code - it's not like they're running in an
interpreter.

The real issue is the GC is not as efficient.

------
userbinator
I thought this article would be about something like
[https://en.wikipedia.org/wiki/Java_4K_Game_Programming_Conte...](https://en.wikipedia.org/wiki/Java_4K_Game_Programming_Contest)
but it's more appropriately "Really Small JVM".

 _For a simple project requiring java.net.http, using these steps produced a
23MB jlink image_

23MB may seem tiny today, but depending on what that "simple project" does, in
absolute terms it's still twenty-three _million_ bytes. For comparison, a full
installation of Windows 3.11 is roughly that size too, _not_ compressed. As
the saying goes, "there's still plenty of room at the bottom."

~~~
hyperpallium
Things That Turbo Pascal is Smaller Than
[https://prog21.dadgum.com/116.html](https://prog21.dadgum.com/116.html)

~~~
NathanielLovin
The wikipedia page for Turbo Pascal, at 43,234 bytes, is bigger than Turbo
Pascal

------
jcollins
I've found a combination of Spring Boot executable jar + a small jlink built
JRE runtime packaged next to the executable jar works fairly well.

Our zipped distribution is 50mb. Uncompressed the executable is 26mb and the
runtime is 75mb.

I'd love for it to be smaller but I can't complain. This also makes solving
for Linux, macOS, and Windows pretty trivial. They each get their own zip with
packaged runtime.

~~~
jwhitlark
This seems to be about the sweet spot that I keep finding, although I normally
work in clojure.

------
stuff4ben
Interesting to see this as I didn't know it was so simple to compile the JDK
these days. But curious how this compares to Quarkus
([https://quarkus.io/](https://quarkus.io/)) which is built upon GraalVM?

~~~
nobleach
I keep a close eye on quarkus.io. I love what they're doing for the Java
ecosystem. It's interesting to see other frameworks follow suit (Micronaut,
etc)

~~~
crazysmoove
Micronaut predates Quarkus by almost a year, but they both seem to be great
frameworks.

~~~
nobleach
Agreed... I almost looked it up before I hit "reply". I didn't start seeing
the container native/GraalVM stuff in Micronaut's tweets until more recently,
but that doesn't mean anything. Good info to know. Thanks!

------
chvid
"Since bundling apps and runtime is the new best-practice"

When did this become the new best-practice in Java land?

~~~
gcbw3
since the author internalized the AWS marketing message.

~~~
retbull
I mean AWS has driven a lot of developer practices in the last 10 years. Not a
terrible bandwagon to jump on.

------
zmmmmm
It's an interesting turnaround. Once upon a time all the thinking was that you
should pile as much into shared DLLs and common runtimes as you can, because
those things will almost certainly be kept in the cache and will be much
faster to launch / lower overhead.

Now not only is it becoming "best practice" to bundle your whole runtime with
your app, people are bundling the whole operating system via Docker.

I'm curious if people have compared actually using a single JVM that is kept
hot in OS cache vs separate minimal packaging for every app. It might favor
single packaging if you only run 1 java app, but if you ran 10 different ones
the benefits of shared code might win.

~~~
a3n
With a JVM kept hot in OS cache, how many different JVM versions would you
need to support apps made with different JVM versions?

~~~
zmmmmm
Ideally one, maybe two? An LTS and a current version?

------
manojlds
Is GraalVM a good option for this?

~~~
hmottestad
Yes and no. Graal Native Image doesn’t work for spring, since some of the jvm
functionality wrt. class loading don’t work yet (I think). Also native image
has lower throughput due to the lack of profile driven code optimizations.

~~~
jacques_chester
The Spring team and Graal folks are working on full support. I believe the
main angle is to ensure the annotation-based model will work smoothly[0].
Another is to introduce a fully-functional model (Spring Fu/Kofu [1]).

Bear in mind, a lot of what Spring -- Spring Boot in particular -- gets
charged with is beyond its control. It's an entrypoint to the vast Java
ecosystem, much of which is also incompatible with Graal native images at the
moment.

[0] [https://github.com/spring-projects-experimental/spring-
fu](https://github.com/spring-projects-experimental/spring-fu)

[1] [https://github.com/spring-projects-experimental/spring-
graal...](https://github.com/spring-projects-experimental/spring-graal-native)

------
sgt
I get Docker and I use it, but it does bother me that one bundles the JDK
along with the docker image. Such a waste of space. I like this idea, I hope
it gets more traction.

In terms of deployment of Java apps, we've found that Nomad works really well
as it doesn't need a full Docker image (or equivalent) like Docker, Kubernetes
etc. In Nomad you just make sure the worker node has the JDK, and then you
specify the .jar file in the job.

~~~
Longwelwind
I'm not sure this is the issue you are referencing, but you can use multi-
stage Docker image builds to avoid bundling the JDK in the final Docker image:

    
    
      FROM openjdk:jdk AS build
      
      COPY . .
      RUN mvn package
    
      FROM openjdk:jre
    
      COPY --from=build ./somepath/app.jar .
    
      CMD java app.jar
    

The first stage (defined by the first FROM) is only used to build the ".jar",
the second stage will only contain the JRE and the ".jar" copied from the
first stage.

~~~
dashwav
Anything after Java 9 would have to use the jlink method, as with the new
modules system, there is no longer a distinction between the JDK and JRE.

The problem is most 3rd party libraries aren't ready for the modules
switchover, so for anyone using those libraries, you are stuck with having
either a 3-400mb docker image or using jlink

~~~
javagram
There are “jre” images for java 10 plus that exclude the java compiler and
other unnecessary JDK components, you don’t need to use modules to use this.

------
malkia
Hmmm.. Makes me wonder if this could help bazel slim down even more (although
it's very acceptable size right now).

