
Using Java 9 Modularization to Ship Zero-Dependency Apps - lfischer
https://steveperkins.com/using-java-9-modularization-to-ship-zero-dependency-native-apps/
======
freedomben
This is definitely an improvement for Java, but it leaves me with a few
thoughts as someone who has done Java professionally at times, but also
professionally lived in other ecosystems including C++/Qt, Python, Ruby,
Golang, and of course node/JS.

1\. Setting up a project is still a pain in the butt. Tools like Gradle are a
nice improvement over Ant (and some would say maven), but still most people
don't even understand them. You have some serious reading ahead of you if you
want to set something up that isn't already templated somewhere for you. You
can lean on an IDE for sure, and for most Java devs this is probably a no-
brainer. I'm weird in that I don't like magic. I prefer to know what the tool
is doing on my behalf, and the Java IDE world is so complex that it isn't
practical to learn that unless you're in the ecosystem for years. Then dealing
with weird exceptions from the JVM can be maddening.

2\. The Java world moves slowly. It could reasonably be years before many
shops transition to Java 9, when you would actually realize the benefits of
this in your work life.

3\. So much Java runs on the server side anyway, where executable size and
entrypoint doesn't really matter that much. Because of this, it may only be a
small subset of Java shops that really get into Java 9/Jigsaw and iron out the
bugs, and create tools/tutorials for others.

~~~
malkia
Currently I'm doing C++ with Qt, sometimes C# (game dev under Windows), but
was at Google just few months ago doing Java.

While Qt is great, we've been running into logistic problems - like we have
plenty of tools that use it, and we are forced to place all the binaries into
one folder ("bin"), and the Qt dlls there for legal reasons, license, but
buying the license for static linking won't be a problem for us reallly. The
real problem is that once we link our binaries statically, then our plugins
that also use Qt won't work with them, unless we do some magic, and forward
all QtXxx.dll end points to be from the main executable, or use more abstract
interfaces - e.g. our plugins should not use Qt directly.

~~~
kodablah
I likewise have used these. Qt (really C++) development in general suffers
from annoyingly slow compile times in my experience. Also, once you go beyond
anything trivial, .pro files become complicated as do build systems around
them.

~~~
malkia
We don't use .pro files or QtCreator - it's Visual Studio + custom build
rules. Still slow, and nasty with generation.

As build system, we used to have IncrediBuild, but since we are PS4 developers
we get SN-DBS for free - [http://www.snsys.com/products/SN-
DBS.asp](http://www.snsys.com/products/SN-DBS.asp) \- it speeds up the build,
but once the Qt moc/etc rules come in, the build needs to wait for these to be
generated, and then proceeds.

There are ways to overcome this, and we are looking into improving this.

I've tried using .pro files, but first, I can't sell them to the rest of the
engineers, as pretty much almost everyone knows Visual Studio, .sln/.vcxproj
files. There are some ideas about alternative build systems, but not there
yet. (at the end most of these just generate .sln/.vcxproj, like CMake, or
premake, etc.).

~~~
kodablah
My application is cross platform. So I use .pro files + qmake, then
make/nmake. As I started needing to move files around in cross platform ways,
I just maintain a Go script now that does the build. Tired of custom DSLs and
having to dig deep into build-system-specific things (e.g. Groovy w/ Gradle
and the confusion around what code runs when like defs vs tasks, doFirst,
etc).

~~~
malkia
Try using "jom" as replacement for nmake - it's done by Qt themselves, and
it's much better! - [https://wiki.qt.io/Jom](https://wiki.qt.io/Jom)

------
jcdavis
Is worth mentioning that SubstrateVM
([https://github.com/graalvm/graal/tree/master/substratevm](https://github.com/graalvm/graal/tree/master/substratevm))
is now open source, which does whole-world AOT compilation to produce a single
executable or shared library. It does have substantial limitations (eg no
runtime class generation/loading), so it might not be suitable for every
project

~~~
mintplant
It's ashame there's no Windows support, unless I've missed something.

~~~
pjmlp
For the time being.

The plan is to support all major OpenJDK platforms.

There is also Project Metropolis, which was recently started, with the long
term goal of rewriting all existing C++ code into Java, so AOT needs to exist
in all platforms.

------
dullgiulio
The author misses the point when he says this might help Java regain share in
the space of DevOps tools which is currently mostly Go.

The problem is not artifact size but the JVM slow start-up time. This is made
worse by almost all Java frameworks that by definition do all their own stuff
before passing control to your real app code.

Such executables are not something you would put in a loop in a shell one
liner, and that is the space for command line utilities Go has occupied
successfully.

~~~
tapirl
In fact, JVM doesn't start slow. The fact is most Java web apps start slow.
The more libraries a Java web app depends, the slower it starts.

The current dominant culture of the Java community is very weird. Although
Java has a huge standard library, if you don't use a lot of third-party
libraries, you will be viewed as a non-professional Java programmer.

Most popular Java tech stack look open sourced, but are backed by lots of
money provided by many companies with bad reputations on pure open source
culture.

BTW, slow start-up is not the only problem of many Java apps. GUI Java apps
often lag for one or more seconds from time to time, which hurts the
experiences of Java GUI apps much.

The third problem of Java apps is they often consume much more memory than
apps written in other languages, in particular for the long running Java apps.
The longer they run, the more memory they will eat.

[edit] the library use way of Java, by putting library jars into your
projects, is not good as the way of other languages, by putting library
sources into your projects. By putting library sources in your projects, you
can view how the libraries are implemented easily. Yes, you can also put Java
library sources into your projects, but the main stream culture of Java
doesn't recommend to do this. Just look at maven.

[edit 2] just my personal opinion. I don't like the way to use a separated VM
to run many apps. This makes it is hard for me to use a different permission
setting for each app. Sometimes I want to block a Java app to access network,
but I must block the whole Java VM to achieve this.

~~~
lmm
> Although Java has a huge standard library, if you don't use a lot of third-
> party libraries, you will be viewed as a non-professional Java programmer.

The standard library is where modules go to die. The fact that this phrase
originates in the Python world tells you that this isn't a Java peculiarity.

> Most popular Java tech stack look open sourced, but are backed by lots of
> money provided by many companies with bad reputations on pure open source
> culture.

The sad reality is that those companies are the only organisations that commit
serious resources to open-source development, outside a couple of specific
niches (web development, unix-like OSes, scientific research up to a point).
Those stacks aren't open-sourced in other languages, they just don't exist.
And the whole point of open-source is that it benefits everyone regardless of
how pure or otherwise the original motives were.

> the library use way of Java, by putting library jars into your projects, is
> not good as the way of other languages, by putting library sources into your
> projects. By putting library sources in your projects, you can view how the
> libraries are implemented easily. Yes, you can also put Java library sources
> into your projects, but the main stream culture of Java doesn't recommend to
> do this. Just look at maven.

WTF are you talking about? You declare your maven dependencies and they can be
resolved as source or binary. In eclipse I can click through to any library
function and see its source immediately. (It's one of the best dependency-
management systems going, in any language; there's a single central repository
that everyone uses in practice, but it's easy to run your own if you want.
It's years ahead of everyone else in terms of package signing. There are
multiple independent codebases using the same repositories, not just in theory
but in practice).

> [edit 2] just my personal opinion. I don't like the way to use a separated
> VM to run many apps. This makes it is hard for me to use a different
> permission setting for each app. Sometimes I want to block a Java app to
> access network, but I must block the whole Java VM to achieve this.

Again completely normal - Python, Ruby, C#, OCaml... will have exactly the
same issue. Get a better firewall.

~~~
tapirl
> You declare your maven dependencies and they can be resolved as source or
> binary. In eclipse I can click through to any library function and see its
> source immediately.

Surely, you can view Java dependency sources. It is just that most Java
programmers don't care about the sources, they are just care about the jars
(the default culture).

In my honest opinion, cross-platform by bytecode has few advantages over
cross-platform by source nowadays, on the other hand, cross-platform by
bytecode has many inconveniences.

And, (in my taste), single central repository is bad. That's why I don't like
Node also. The npm central repository is the source of many Trojans. Many Java
and Node developers are not aware of and have no ideas on what they have
downloaded through chain dependencies.

> Again completely normal - Python, Ruby, C#, OCaml... will have exactly the
> same issue. Get a better firewall.

Normal != good.

~~~
lmm
> Surely, you can view Java dependency sources. It is just that most Java
> programmers don't care about the sources, they are just care about the jars
> (the default culture).

Not my experience. Actually getting to the source of your dependencies, in
practice, is easier in Java - just one click in your IDE - than in any other
language. Doesn't that suggest that Java programmers care more about sources
than other language users, not less?

> In my honest opinion, cross-platform by bytecode has few advantages over
> cross-platform by source nowadays, on the other hand, cross-platform by
> bytecode has many inconveniences.

Well you're entitled to your opinion but you should probably back it up with
something more specific if you want to convince anyone else.

> And, (in my taste), single central repository is bad. That's why I don't
> like Node also. The npm central repository is the source of many Trojans.
> Many Java and Node developers are not aware of and have no ideas on what
> they have downloaded through chain dependencies.

You don't have to use a central repository if you don't want to, and some
people don't, but it's very convenient to have the option if you want it.
Maven central requires PGP signatures on anything published there, so it's
very easy to enforce that all the dependencies you depend on come from trusted
people if you really want to - as far as I know there's no non-JVM language
that does that, certainly not with anything like as large a base of signed
libraries available.

> Normal != good.

Agreed, but you started out by claiming that Java's culture is very weird. It
isn't.

~~~
tapirl
> Well you're entitled to your opinion but you should probably back it up with
> something more specific if you want to convince anyone else.

The only benefit of bytecode is to save compiling time. Nowadays, CPU becomes
much faster than the time Java was invented, 20 years ago. So the benefit is
weak now. Another promoted benefit of bytecode is compile-once-run-anywhere.
However, I think this is a hoax. It is not special, for script languages code
runs anywhere without compiling.

~~~
readittwice
> Another promoted benefit of bytecode is compile-once-run-anywhere. However,
> I think this is a hoax.

I was once working on a large project on the JVM: our Fat-JAR was really
working on Linux, Mac and Windows. The exact same JAR. And it didn't matter on
which system we have built that JAR. This may not be possible for every use
case - but I don't think it is a hoax.

> It is not special, for script languages code runs anywhere without
> compiling.

IMHO scripting languages like Ruby, Python, etc. are quite hard to deploy,
especially if they have dependencies. In comparison to languages like Java, Go
or Rust.

------
digitalsanctum
I've recently become much more interested in Kotlin to provide true native
apps. The trade-off is that the native bits are still pretty green.

~~~
kodablah
And the GC and the minimal stdlib and other things. If you are targeting
multiplatform, Kotlin may be a reasonable choice so long as you don't need
anything advanced the JRE provides (or are willing to abstract it and have a
native equiv also). Otherwise, there are too many native options these days to
use Kotlin solely for that purpose IMO.

------
nine_k
Now combine it with JavaFX and we can have reasonably-sized cross-platform GUI
apps again.

Yes, you can compile a QT or a GTK app for every platform, and use e.g. Python
or Go to write the cross-platform part + package it into a single file. It's
still as many files as you have platforms.

~~~
cpburns2009
How easy is it to get a native look and feel with JavaFX on the various
desktop OSes? That is, Windows, Mac, and Linux (GTK and Qt)?

~~~
consto
In my limited experience, it's hard, but in most cases that isn't a problem.
JavaFX is powerful, separating layout and styling from code via FXML and their
CSS like language. If you want to style the application like a webapp it's a
piece of cake, but if you want to style it to look native then you'll have to
use stylesheets such as [http://www.aerofx.org/](http://www.aerofx.org/)

On the otherhand, with Swing all you need to do is add the following line:

    
    
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

~~~
cpburns2009
Okay, so for JavaFX you would need a specific style sheet for each OS version
provided it doesn't support any theme customization. That's less than ideal,
and a step backwards compared to Swing or SWT.

------
drraid0
Are the installers for such "native" apps going to ask me to install an
Ask.com toolbar?

~~~
simooooo
Shit like that removes any interest for me to look into Java

~~~
windust
Installing the JDK has no ask toolbar (vs the JRE which is just the client
libs)

------
malkia
How are .so, .dll files handled during Java packing? This is more open
question, but say you have one ".exe" file. Now you need to extract the DLL in
a place, and make sure everytime it's the right one (gets tricky if the tool
is ran simualtnenously, like spawned from a build process).

While I was at Google, instead of having multiple .so files, the main launcher
was a C++ binary, and all external C++ dependencies were linked into it
(that's it if you have the source code), then you didn't need that.

I guess now it's up to the BUILD system to achieve that, where if you have
more flexible BUILD language you can instruct everything to go in the main
launcher file, then have all the .jars into one, slapped at the end of the
file.

At the end, ship or update only one binary to your service, desktop machine,
etc.

~~~
zten
> How are .so, .dll files handled during Java packing?

Native libraries used by JNI can be shipped in the jar files and are read from
the classpath, in addition to reading them from the ordinary linker path.

(edit: turns out I'm wrong about this on Windows)

~~~
malkia
But on Windows you must "extract" and copy the DLL file somewhere (ok,
possibly you might be able to do in-memory load, but that is very non-standard
and might trigger antiviruses).

I think the same is with the Python packagers, or C# shadow copy.

~~~
weej
You need to package up the JAR with the shared object / dll and install both
on the local filesystem in order for the SO to be accessible from the
classpath. For example you could apply a package manager (RPM) and include the
JAR and .so at install for available use.

~~~
malkia
But if you were able to "contain" all the source code from your DLL into your
main "java.exe" launcher, and then slap the all the .jars at the end of the
"java.exe" and then renamed it to "mycoolapp.exe" then it might be just
that... off course if licensing agrees with you, and you have the source code,
and want to deal recompiling these like that... also compiling it along with
the java launcher.

This way there is no need for extra install, uninstall. Possibly too much
over-engineering for full blown product, but if it's something that needs to
be run, and updated on tons of machine - not having to deal with extra
artifacts might be a win.

------
Ecco
I love how the article shows an 11 MB Hello World as "stripped down" 2x
improvement :-)

~~~
nine_k
The VM + minimal stdlib is not exactly small. Also AFAICT it works the same on
every major platform, that is, it's a fat binary. Not bad.

~~~
kevin_thibedeau
> Not bad.

Unless you consider that basic support services are what the Java runtime
should be providing on its own. Fundamentally, a command line Hello world
should only take a few dozen bytes of bytecode.

~~~
oblio
Doesn't this bundle the VM/GC/etc. besides the bytecode?

~~~
paulddraper
Yes, it does.

------
malkia
This would be great for BUILD tools like bazel which is written in Java.

~~~
mkobit
Do you mean for Bazel itself or for building apps with bazel?

Bazel is still a bit away from having Java 9 support [1] and it still requires
some system dependencies (for the other languages and cross system support).

I wish more tools would go the "static binary" path that make it easy to build
and use wrappers without having to figure out system configuration every
single time.

[1]
[https://github.com/bazelbuild/bazel/issues/3410](https://github.com/bazelbuild/bazel/issues/3410)

~~~
malkia
bazel itself. One of the complaints I here (not sure how valid they are) is
that people's docker images tend to grow a lot, and I'm not proposing bazel
yet, as it'll pull additional hundreths of megabytes, plus some more per
WORKSPACE.

For Windows, it's mainly perception - if engineer sees that a tool is like
200mb in the depot, he/she be like - that's too much :) "Remember why we
pulled boost, and no longer use it - as it's huuuge!" :)

------
e12e
Previous discussion:

[https://news.ycombinator.com/item?id=15521611](https://news.ycombinator.com/item?id=15521611)

------
stevefan1999
I reckon Mono had offered the same thing, although it bundles the entire
mscorlib...

------
fulafel
Does this affect Clojure? If so, how?

~~~
marmaduke
For the purposes of this article, Clojure is "just" a library to process text,
generate & run JVM byte code, and the workflow could be applied to a Clojure
project.

The AOT compiling though would require Clojure-AOT followed by JVM-AOT I
guess.

------
alfanick
> Using Java 9 Modularization to Ship Zero-Dependency _Native Apps_

> [...]

> and superior to web-hybrid options like Electron

How is Java or Electron (JavaScript+WebKit) considered _native_ nowadays?

Please use native APIs, so my laptop can actually hold 10h load on battery.

edit: can we remove _native_ from the title?

~~~
kuschku
Java can be compiled to a native binary now, thanks to Java 9. No bytecode,
interpreter, or JIT left in the resulting file. (Only available for Linux
x86_64 right now).

~~~
readittwice
If you are talking about Java's AOT feature (JEP 295:
[http://openjdk.java.net/jeps/295](http://openjdk.java.net/jeps/295)) then I
don't think it is true AFAICS. The JVM is still needed, Java-Code can be
compiled into a shared library but this is "just" a code-cache that can be
passed to the JVM when starting the application. This feature is mainly aimed
for improving startup.

~~~
kuschku
I’ll link you the beautiful thing I’m referring to:
[https://github.com/graalvm/graal/tree/master/substratevm](https://github.com/graalvm/graal/tree/master/substratevm)

~~~
readittwice
Ah thanks, I got confused because you mentioned Java 9. I really hope
Substrate VM or something similar gets into the JDK. Having the option of AOT-
compilation or using jlink for bundling would be amazing.

------
adamzegelin
Dunno if I'd call Java _native_. This is more like zero-dependecy Java apps —
aka, you don't need to pre-install the JVM (and its "optional" adware on
Windows).

~~~
SwellJoe
Does that mean Go apps aren't native since they also have a runtime embedded?
What defines "native"?

~~~
chrisseaton
> What defines "native"?

I think it's reasonable to say that if the the user code is transmitted as a
managed intermediate representation then it's not native. Arguing beyond that
probably isn't productive.

But that's this particular blog post. Java can be native, if you compiled all
the user code to machine code ahead of time.

~~~
SwellJoe
So, anything that uses a JIT, such as the LLVM JIT, is not "native"? Are
programs written in C# not native, even on Windows? Are programs in Java for
Android not native, even though it is the language much of the OS is written
in and what the standard UI toolkit is built for?

Seems like lots of projects are likely to become non-native over time, by this
definition, even if they start out "native" with a basic C/C++ codebase, since
lots of projects accrete some sort of scripting or extension interface that
might use a runtime of some sort. Is Emacs not native? Does it really matter
if the runtime (which _is_ machine code) was made by the compiler developer or
by the application developer in some cases?

I feel like there's an arbitrary line, and it's not entirely useful
distinction to make.

A native app can be big and slow and ugly and fail to follow the OS UI
guideliness. An app with a runtime can be small(ish) and fast and beautiful
and strictly adhere to the UI guidelines for the OS in question. I want the
small, fast, beautiful, app with standard UI behavior, no matter whether it's
delivered with a runtime embedded or built from C/C++ (and a few others) and
without a JIT.

I mean, we're just arguing semantics here, which is kinda silly. I think it's
pretty neat; I don't really like working in Java, but I'd rather work in Java
than C/C++, so if I needed to deliver a cross-platform GUI app, I'd certainly
consider it.

~~~
igravious
> So, anything that uses a JIT, such as the LLVM JIT, is not "native"?

Correct.

> Are programs written in C# not native, even on Windows?

No they are not, because .Net uses an intermediate representation, being
Windows and Microsoft confers no magic "native"-ness.

> Are programs in Java for Android not native, even though it is the language
> much of the OS is written in and what the standard UI toolkit is built for?

Programs in Java for Android are not native.

Much of the kernel (Linux) is written in C. According to "Francesco Iovine,
AndroidStudio lover" over at Quora[0] the UI toolkit is written in Java and
the virtual machines (Dalvik and Art) are written in C++.

> Is Emacs not native?

Emacs is a strange and evil beast whose name we shall not invoke. No, but
seriously – I imagine Emacs is a hybrid native / Emacs lisp program. Many
programs with embedded scripting languages are hybrid beasts but they ought to
be thought of as native apps if they are compiled for a certain bare metal
hardware platform. I presume the core of Emacs is C. Ergo, native.

> Does it really matter if the runtime (which is machine code) was made by the
> compiler developer or by the application developer in some cases?

Yes, I believe it does matter.

> I feel like there's an arbitrary line, and it's not entirely useful
> distinction to make.

No, I don't believe it's arbitrary.

> A native app can be big and slow and ugly and fail to follow the OS UI
> guideliness. An app with a runtime can be small(ish) and fast and beautiful
> and strictly adhere to the UI guidelines for the OS in question. I want the
> small, fast, beautiful, app with standard UI behavior, no matter whether
> it's delivered with a runtime embedded or built from C/C++ (and a few
> others) and without a JIT.

True. But that is neither here nor there, that's a whole other issue.

> I mean, we're just arguing semantics here, which is kinda silly.

No, we are not. A native app is one which is either hand-crafted assembly for
a specific hardware platform or compiled ahead of time targeting a specific
platform. Native code is non-portable, the source code might be portable, but
generally is not unless care is taken. Assembly code is never portable. Non-
native (whether by VM: Java, C#, … or interpreted: Ruby, Python, …) tends to
be more portable.

[0] [https://www.quora.com/What-programming-language(s)-is-
Androi...](https://www.quora.com/What-programming-language\(s\)-is-Android-
written-in)

~~~
kazinator
Suppose I write a C program and then deploy it on a target machine where it is
continuously running thereafter. Then I develop a loadable module (.so) which
I compile, upload to the target and get the C program to load it with
_dlopen_. Is that native or not?

If it's native, why isn't JIT to machine code not native? Because of the
convenience of all of the above happening on a fine-grained level (individual
functions), and all on the target machine?

Also, what is "compiled ahead of time"? Ahead of what time? Why is that
specific time important? Obviously, code is compiled ahead of its compiled
image being run; the compiled version cannot be run any sooner. Any JIT-ted
function is compiled ahead of being called.

On most operating systems, we can load programs after the OS boots. Those
programs can be developed after the OS booted.

I think that the only true definition of "native" is that the program image
was compiled before the hardware was built and turned on for the first time,
and was present in its ROM.

Anything else is not "ahead of time" and not native, damn it!

Installing a program on your PC is just a form of JIT. The system was fired up
without that program being there, and the program was snuck in just moments
before the user's intent to use that program. Not ahead of the True Scotsmans
proper time: the time when components were stuffed into the circuit board and
it was powered up.

~~~
chrisseaton
I think you're looking a bit too manically for holes in what is a pretty
informal term that's useful in everyday discussions. It's not a mathematical
proof that you're a genius for finding a gap in.

~~~
kazinator
The term is misused. Native means "native machine code", in any shape or form
with no interpretation layer in between. OP seems to be confused between
native vs. non-native axis and monolithic/static vs. modular/dynamic.

If the software image is compiled in its entirety before being installed on a
target, that is "monolithic". If parts of it can be dynamically loaded, it is
"modular". Dynamic loading of functions with JIT is just heaping on more
modularity: hyper-modular.

A situation involving JIT is non-native only in the sense or to the extent
that there is non-JIT-ted code in the image. If there is an interpreter so
that some things execute without being JITted, those things are not native.

~~~
igravious
I don't believe that the term is not misused. What is happening perhaps is
that you disagree with the term. And that's fine, that's your prerogative. I
don't think that I am confused.

------
LoSboccacc
but why gradle?

~~~
emsy
This comment adds nothing unless you name specific criticism or ideas for
improvement.

~~~
yarrel
That answer does not explain the choice of technology.

~~~
emsy
I'm not obliged to. I just wanted to help the previous commentator out because
I knew this would earn them negative karma for what could be a constructive
discussion.

------
iamleppert
"and superior to web-hybrid options like Electron"

Superior exactly in what way? Electron allows access to an enormous audience
of developers and the web ecosystem. You really can't compete with that and
would be silly to try at this point.

~~~
Negitivefrags
You cut out the rest of the sentence which was specifically referring to on-
disk size.

~~~
iamleppert
It's not valid (or even remotely tangential) to compare a hello world app with
minimal GUI to a full web browser.

~~~
kuschku
The Java standard libraries also contain a full web browser, and much more
than what your browser can do.

~~~
guelo
Sounds like you didn't read the article.

~~~
kuschku
The point is that even if you add the module for the browser, you’re still
significantly smaller than Electron – while, at the same time, Java can
actually eliminate unused modules for you, which Electron doesn’t.

------
sillysaurus3
Anyone notice that Java is suddenly cool thanks to Kotlin?

I haven't done much more than dabble, but it was an enjoyable dabble. Unlike
vanilla Java.

It even compiles to JS with a relatively small-ish runtime. It's big enough to
cause problems embedding it, but it's small enough to be workable.

[https://github.com/JetBrains/create-react-kotlin-
app](https://github.com/JetBrains/create-react-kotlin-app)

