Understanding the Java JMOD File Format
With the introduction of the Java Platform Module System (JPMS) in Java 9, modularity became a core feature of Java. This brought new packaging formats, one of which is the JMOD file format. JMOD files are primarily used during the development and packaging phase of Java applications. They are not meant to be distributed directly to end users but instead serve as an intermediate format for creating custom runtime images. Let us delve into understanding the Java JMOD file format and its role in building modular and optimized runtime environments.
1. Understanding the JMOD File Format
A JMOD file is a packaging format introduced in Java 9 as part of the Java Platform Module System (JPMS). It extends the capabilities of traditional JAR files by allowing developers to bundle not just compiled Java classes, but also native code, configuration files, and other platform-specific resources into a single module package.
Unlike JAR files, JMOD files are not meant for direct distribution or execution. Instead, they are used during the build and packaging phase, especially by tools like jlink, to create optimized and custom Java runtime images tailored to a specific application.
JMOD files are typically located inside the JDK installation directory under: $JAVA_HOME/jmods. Each module of the JDK (such as java.base, java.sql, etc.) is packaged as a JMOD file, which allows fine-grained control over what gets included in a custom runtime.
1.1 Structure and Contents of a JMOD File
A JMOD file is essentially a structured archive (similar to ZIP) with predefined sections. These sections allow different types of resources to be organized efficiently:
classes/→ Compiled.classfileslib/→ Native libraries (.so,.dll,.dylib)bin/→ Native executables or scriptsconf/→ Configuration filesinclude/→ Header files (used for native development)legal/→ Licensing and legal notices
This structured layout makes JMOD more versatile than JAR when dealing with platform-dependent components.
1.2 Core Features and Characteristics
- Richer Packaging → Supports classes, native code, configs, and more in one module
- Platform-Specific → Can include OS-specific binaries (Linux, Windows, macOS)
- Not Executable → Cannot be run directly using
java -jar - Build-Time Usage → Primarily used during linking phase with
jlink - Strong Module Integration → Fully aligned with JPMS module descriptors
- Improved Optimization → Helps create smaller, optimized runtime images
1.3 When to Use JMOD in Practice
- When your application depends on native libraries
- When building custom runtime images using
jlink - When working with platform-specific deployments
- When you need fine control over module packaging
1.4 Comparing JMOD and JAR
| Feature | JAR | JMOD |
|---|---|---|
| Primary Purpose | Application/library distribution | Module packaging for runtime linking |
| Execution | Runnable (java -jar) | Not directly executable |
| Distribution | Yes | No (internal use only) |
| Native Libraries | Limited / indirect | Fully supported |
| Platform-Specific Content | No | Yes |
| Used by jlink | No | Yes |
| Module Awareness | Optional (via module-info) | Fully modular |
1.5 Key Takeaways
Although JMOD files are powerful, they are not a replacement for JAR files. Think of JAR as a distribution artifact, while JMOD is a build-time artifact used to assemble a runtime environment.
2. Practical Example: Creating a Custom JRE
Let us walk through a practical example where we create a simple modular Java application, package it into a JMOD file, and then use jlink to build a custom Java Runtime Environment (JRE) tailored specifically for the application.
2.1 Defining the Module Structure
2.1.1 Creating module-info.java
Below is the module descriptor that defines and exports the module.
// module-info.java
module com.example.hello {
exports com.example.hello;
}
This file defines a Java module named com.example.hello. The module keyword declares the module, and the exports statement makes the package com.example.hello accessible to other modules, enabling proper encapsulation and modular design under JPMS.
2.1.2 Creating the Main Class
Below is the main class that serves as the entry point of the application.
// HelloWorld.java
package com.example.hello;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello from JMOD example!");
}
}
This is a simple Java class inside the com.example.hello package. It contains the main method, which serves as the entry point of the application and prints a message to the console, demonstrating a basic modular Java program.
2.1.2 Compiling the Modular Application
The following command compiles all module-based Java source files.
javac -d out --module-source-path src $(find src -name "*.java")
This command compiles all Java source files in the src directory using module-aware compilation. The -d out option specifies the output directory for compiled classes, while --module-source-path tells the compiler where to find module sources, ensuring proper handling of the module structure.
2.1.3 Packaging the Module into a JMOD File
The following command packages the compiled classes into a JMOD file.
jmod create --class-path out/com.example.hello \
--module-version 1.0 \
mods/com.example.hello.jmod
This command creates a JMOD file from the compiled classes located in out/com.example.hello. The --module-version flag assigns a version to the module, and the resulting .jmod file is stored in the mods directory for use in building custom runtime images.
2.1.4 Building a Custom Runtime with jlink
The following command creates a minimal custom Java runtime image.
jlink --module-path $JAVA_HOME/jmods:mods \
--add-modules com.example.hello \
--output custom-runtime
This command uses the jlink tool to generate a custom Java runtime image. It combines JDK modules (from $JAVA_HOME/jmods) and the custom module (from mods), includes only the required module com.example.hello, and outputs a minimal runtime in the custom-runtime directory.
2.1.5 Running the Application Using Custom Runtime
The following command runs the application using the generated runtime.
./custom-runtime/bin/java -m com.example.hello/com.example.hello.HelloWorld
This command runs the application using the custom runtime image. The -m option specifies the module and the main class in the format module/class, ensuring the program executes within the modular runtime environment created by jlink.
2.1.6 Output
Hello from JMOD example!
This output is generated when the application is executed using the custom runtime image created with jlink. It confirms that the modular application has been successfully compiled, packaged into a JMOD file, linked into a custom Java runtime, and executed correctly, with the HelloWorld class printing the expected message to the console.
3. Conclusion
The JMOD file format is a powerful addition to Java’s modular ecosystem, offering a flexible way to package modules along with native libraries, configuration files, and platform-specific resources, which makes it especially useful for creating optimized runtime environments. Although JMOD files are not meant for direct distribution or execution, they serve an important role during the build phase, particularly when used with tools like jlink to assemble custom Java runtime images tailored to specific applications. For modern Java applications, including microservices and containerized deployments, leveraging JMOD along with modular runtimes helps reduce application size, improve startup time, and enhance overall performance by including only the necessary components.

