Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

These days, all Java runtimes are created with jlink, including the one that's bundled in the JDK, so it's worth it to take a few minutes to learn how to do it yourself for a custom image. The result is not only drastically smaller, but more secure, as the potential attack surface area is much smaller.

If your application is modularised, it can become a part of the image, but, as the article shows, creating a custom runtime is easy and recommended even if your application is not modularised.

BTW, the JDK contains not just a Java runtime, but development tools, as well as an additional copy of the entire class library (this copy, in jmod files, is stored in a format that jlink uses as its input; i.e. it's there only to allow generating new runtime images). Use the entire JDK as an runtime is a real waste of space, since, among other things, it contains all libraries twice.

One minor comment, though. jlink produces Java runtime images or, in short, Java runtimes -- not a JRE. The name JRE refers to a particular kind of Java runtime from a bygone era when there was a global Java runtime environment, which was used by applets (and Web Start applications). When applets and Web Start were removed, the JRE and the very concept of one, was gone along with them. Some people still use the anachronistic term JRE to refer to a Java runtime (and some companies distribute pre-linked Java runtimes and call them JREs), but the real JRE is gone.



How is modularization progressing in practice?

Stuff like Hibernate, Spring, the big libraries.

How easy is it for the average greenfield enterprise to start as a modular app?

How easy is it for the average brownfield enterprise app started 10 years ago to convert to a modular app?


First, just to repeat, you don't need to modularise your app to use jlink. You modularise to enjoy additional security and evolution benefits.

> How easy is it for the average greenfield enterprise to start as a modular app?

As easy as a non-modularised one.

> How easy is it for the average brownfield enterprise app started 10 years ago to convert to a modular app?

This one is harder to answer because you can only modularise (i.e. encapsulate in modules) code that is actually modular (cleanly separates API and implementation into different packages, no circular dependencies among components that are to become modules etc.). So it depends on how modular your codebase already is. In many situations it could require a not-insignificant refactoring, and so worth it if you really want the best security and encapsulation (which was the case for the JDK itself, which is now fully modularised). In other situations it could make sense to leave things alone and only modularise new project components (modules and code outside modules can mix).

Either way, modular or not, using link to produce a custom runtime is a good idea!


It's getting better to be sure.

It's definitely taken time for the libraries to catch up, but when 17 came out and put a kibosh on the work arounds folks relied upon when using libraries that were not properly modularized.

From a JakartaEE perspective, that's a different world. The containers were modular runtimes of their own sort anyway, many are built upon OSGI, etc. I don't think mainstream EE projects are using modular projects.

As a developer at the end of the line, building personal and in house projects, I don't personally get much value from the modular system. It's extra bookkeeping to manage, it's seems as much whack-a-mole of updating the module-info file as any deterministic plan. You mostly learn you have a problem when something won't load, making the whole thing rather reactive.

I don't really care about the JDK/JRE size, I don't distribute projects that way. They're just jars, or they're WARs for the container. If you have to send up the entire runtime, all the time, then sure there can be value. But as mentioned, you don't need a modular project to use jlink.

So, clearly, the JDK certainly benefits from this. But it's imposed from above on to everything, and I'm not that sure that everyone else gets real value from it.


It's a real shame that modularization focused almost entirely on solving the JDK vendor's problems rather than solving the application author's problems.

Specifically, modularization can and should have been taken as a chance to allow multiple versions of the same dependency to exist within a single scope within an application. At present, this is sort of achievable by taking control of classloading and invoking defineModulesWithManyLoaders, but not many people will do this and key features are missing (it still does not allow a single module to directly reference two versions of the same dependency).


> Specifically, modularization can and should have been taken as a chance to allow multiple versions of the same dependency to exist within a single scope within an application....

Modules do it to the best extent possible, but they don't make it prominent because, quite simply, it's impossible to do in a way that's good enough to be advisable. In order for a library to be properly isolatable, it has to be written in a specific way and be quite limited. Any kind of input or output, including the reliance on configuration outside of the source code can immediately make the library susceptible to horrible problems when multiple copies of it exist in the same process, even if isolated. So modules allow you to use class loader isolation if you really need it, but they don't try to pretend it can actually work in general.

The main (though not only) thing that modules exist to provide is strong encapsulation, which is crucial for security and makes code evolution much easier.


It seems to work just fine without anything horrific happening in Rust. Direct dependencies can be renamed and indirect dependencies simply don't clash with each other.

While libraries directly parsing some sort of conf file may be a little bit of a pattern on the JVM, it doesn't have to be... And the libraries could certainly define a new pattern to allow the user to define different versions of their configs for different library versions.

I agree that, as it stands, modules enhance security by providing a stronger version of the visibility system. IMO that is important but it's just such a low bar to shoot for since it doesn't really improve the situation much more than everyone just following the rules


> It seems to work just fine without anything horrific happening in Rust. Direct dependencies can be renamed and indirect dependencies simply don't clash with each other.

It suffers from the same problems -- doesn't work if a library has an implicit assumption it's the only copy, and does otherwise. It's the best anyone can do, and modules do it as well as it can be done, it's just not something you want to encourage because there are too many cases where even the best is just not good enough. I don't think that instantiating multiple instances of a library whenever there's a version conflict is the right default. It turns an accidental configuration situation into a significant runtime choice.

Still, if you want a tool that makes this best-effort attempt easier, take a look at layrry: https://github.com/moditect/layrry. It basically exposes module's "multi-versioning" as a configuration rather than programmatically.

> IMO that is important but it's just such a low bar to shoot for since it doesn't really improve the situation much more than everyone just following the rules

Actually, it's something that can only be done deep in the VM. Modules exist to provide some runtime guarantees that nothing else can (except the SecurityManager, but that was so problematic that few used it and fewer still did it correctly). Without modules, there can be no strong security guarantees made.


As far as I can tell, Layrry does not allow two versions of the same library to be in scope at the same time.


I don't know what you mean by "in scope" but modules do allow multiple versions to exist in the same process in different layers. That's what module's entire layer mechanism is about (internally, it uses class-loader isolation), and Layrry configures module layers (that's where it gets its name). It's in one of the first examples: https://github.com/moditect/layrry

So modules absolutely do support that by design, but again, for that to work well you need to really know what that duplicated library does (same goes for Rust or any language), and it's not a recommended practice in general because it can fail in really horrible ways if you're unaware of some implicit assumptions in the library.


Even without being modular, switches like --strip-debug --no-man-pages --no-header-files net you a fair amount of space saving.


> Some people still use the anachronistic term JRE to refer to a Java runtime

Starting with Oracle which, on its Java download page, offers the choice to download the "JRE for consumers" so there's that.


That actually is the JRE for Java 8, the last JRE.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: