What does jlink do, and why would you build a custom runtime image instead of shipping a full JRE?
answer
- Assembles a minimal runtime image = trimmed JVM + only needed modules
- --module-path + --add-modules (roots) -> transitive closure -> --output
- Smaller than full JRE, self-contained, smaller attack surface
- Link-time plugins: --compress, --strip-debug, --launcher
- Requires modular inputs (or jdeps/automatic-module route)
basics
~20 sjlink is a JDK tool that assembles a custom, minimal Java runtime image containing only the modules your application actually needs, plus the JVM. The result is a smaller, self-contained runtime you can ship without requiring a separate JRE installation.
solid answer
~40 sjlink links a set of modules (your app's modules plus their transitive dependencies down to java.base) into a self-contained runtime image — a directory with a trimmed JVM and only the required modules. You give it a module path and a --add-modules root set; it resolves the transitive closure and emits a runnable image with its own bin/java. Benefits: a much smaller footprint than a full JRE (often tens of MB), no need for a pre-installed Java on the target, faster startup options, and a reduced attack surface since unused modules aren't present. Common flags trim further: --strip-debug, --compress, --no-header-files, --no-man-pages, and --launcher to generate a named startup script. The key precondition is that everything must be modular (or run jdeps first); plain classpath JARs need the automatic-module or jdeps route.
code
java · 11 lines// Build a minimal runtime image for a modular app:
// jlink \
// --module-path "$JAVA_HOME/jmods:out/modules" \
// --add-modules com.example.app \
// --launcher run=com.example.app/com.example.app.Main \
// --strip-debug --no-header-files --no-man-pages --compress=zip-6 \
// --output myapp-runtime
//
// Run it with NO separate Java installed:
// ./myapp-runtime/bin/run
// (or) ./myapp-runtime/bin/java -m com.example.app/com.example.app.Maingo deeper
Knows jlink makes a smaller, standalone Java runtime containing only what the app needs.
Can write a jlink command with --module-path/--add-modules/--output, explain the size and self-containment benefits, and knows inputs must be modular.
Uses link-time plugins (compress/strip-debug/launcher), handles non-modular dependencies via jdeps/automatic modules, builds cross-platform images, and pairs jlink with jpackage in a release pipeline.
Designs the org's deployment artifact strategy (jlink images in containers, attack-surface reduction, reproducible cross-target builds), and weighs jlink vs full-JRE base images vs AppCDS/native-image tradeoffs.
## The problem jlink solves Before Java 9, you shipped or required a **JRE** (Java Runtime Environment): the whole standard library plus the JVM, on the order of ~200 MB. Most applications use only a fraction of it. **jlink** lets you build a **custom runtime image** containing *only the modules you actually need*, bundled with the JVM, as a standalone directory you can copy and run. Key terms: - **JVM**: the Java Virtual Machine, the engine that executes bytecode. - **Module (JPMS)**: a named unit declaring `requires`/`exports`. Since Java 9 the JDK itself is split into modules; `java.base` is the always-present root every module implicitly requires. - **Transitive closure**: starting from your chosen "root" modules, the full set you reach by following every `requires` edge. jlink must include all of them or the image won't run. - **Runtime image**: the output directory — it has `bin/java`, a `lib/` with a packed `modules` file, and a `release` file. It is self-contained: no external JDK/JRE needed. ## How you run it ``` jlink --module-path $JAVA_HOME/jmods:out/modules \ --add-modules com.example.app \ --output myapp-runtime ``` - `--module-path`: where to find modules (the JDK's `jmods` directory plus your own). - `--add-modules`: the **root** module(s); jlink computes the transitive closure from these. - `--output`: the directory to create. Then `myapp-runtime/bin/java -m com.example.app/com.example.app.Main` runs your app with no separate Java install. ## Why it's worth it 1. **Size**: images are commonly 30–60 MB versus ~200 MB for a full JRE — great for containers and edge deployment. 2. **Self-contained distribution**: the target machine needs no pre-installed Java; you ship one artifact. 3. **Reduced attack surface**: modules you don't include (e.g. scripting, CORBA-era pieces) simply aren't there to exploit. 4. **Optimizations**: jlink runs **plugins** at link time — `--compress=zip-6` (or `2` on older JDKs) to shrink, `--strip-debug` to drop debug info, `--no-header-files`/`--no-man-pages` to omit dev artifacts, and `--launcher name=module/mainclass` to generate a convenient launch script. ## Preconditions and gotchas - **Everything must be modular.** jlink works on modules, so your app and its libraries need module descriptors. If a library is a plain classpath JAR, you must either rely on it as an *automatic module*, modularize it, or use `jdeps --generate-module-info` to draft a descriptor first. - **Cross-target images**: you can build an image for a *different* OS/arch by pointing the module path at that platform's `jmods` (cross-linking), since the modules carry native code per platform. - jlink is a **link-time** step, distinct from runtime tools like `java`; it produces the thing you later run. It pairs naturally with **jpackage** (which wraps an image into a native installer). ## Relationship to the other tools In the JPMS toolchain: **jdeps** tells you *what you depend on*, **jmod** packages a module (with its native libs/config) into a `.jmod` file consumable by jlink, and **jlink** assembles those into the final runtime image.
- What must be true of your dependencies before jlink can include them?They must be modules (have a module-info, or be usable as automatic/explicit modules). Plain classpath JARs aren't linkable directly — modularize them or generate a descriptor with jdeps first.
- Name two link-time optimizations jlink can apply and what they do.--strip-debug removes debug attributes to shrink class files; --compress packs the modules file (and --no-header-files/--no-man-pages drop dev artifacts). --launcher generates a named startup script.
- How do jlink and jpackage relate?jlink produces a self-contained runtime image; jpackage can take an app + (optionally a jlink) image and wrap it into a platform-native installer/bundle (.dmg, .msi, .deb).
saying these in an interview costs you the question
- Saying jlink runs or compiles your app — it links modules into an image at build time
- Claiming you can jlink an arbitrary classpath app with no modularization at all
- Confusing jlink (runtime image) with jpackage (native installer/bundle)
- Thinking the image still needs an installed JRE to run — it is self-contained