Native Images
JBang supports creating native images using GraalVM’s native-image
tool, allowing you to compile your Java scripts into standalone native executables.
What are Native Images?
Native images are compiled, standalone executables that:
-
Start instantly (no JVM startup time)
-
Use less memory
-
Don’t require Java to be installed
-
Are platform-specific binaries
Perfect for CLI tools, serverless functions, and anywhere startup time matters.
Prerequisites
You need GraalVM with native-image
installed:
Installing GraalVM
Option 1: SDKMAN (Recommended)
sdk install java 21-graal
sdk use java 21-graal
gu install native-image
Option 2: Manual Download
1. Download GraalVM from https://www.graalvm.org/downloads/
2. Set GRAALVM_HOME
or ensure native-image
is in PATH
3. Install native-image: gu install native-image
Option 3: Package Managers
# macOS with Homebrew
brew install --cask graalvm/tap/graalvm-jdk21
export GRAALVM_HOME=/Library/Java/JavaVirtualMachines/graalvm-jdk-21.jdk/Contents/Home
$GRAALVM_HOME/bin/gu install native-image
# Linux distributions
# Follow GraalVM documentation for your distro
Creating Native Images
Basic Native Image
# Compile to native image
jbang --native hello.java
# Run the native executable
./hello
The native executable will be created in the same directory as your script.
Native Image Options
Control native image compilation with //NATIVE_OPTIONS
:
///usr/bin/env jbang "$0" "$@" ; exit $?
//NATIVE_OPTIONS -O2 --no-fallback --enable-preview
class FastApp {
public static void main(String[] args) {
System.out.println("Native app running!");
}
}
Or from command line:
jbang --native --native-option="-O2" --native-option="--no-fallback" app.java
Common Native Image Configurations
CLI Applications
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS info.picocli:picocli:4.6.3
//DEPS info.picocli:picocli-codegen:4.6.3
//NATIVE_OPTIONS --no-fallback -H:+ReportExceptionStackTraces
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Parameters;
@Command(name = "greet", mixinStandardHelpOptions = true)
class GreetApp implements Runnable {
@Parameters(description = "Name to greet")
String name = "World";
public static void main(String[] args) {
new CommandLine(new GreetApp()).execute(args);
}
public void run() {
System.out.println("Hello " + name + "!");
}
}
PicoCLI Native Images: Always include
|
Reflection-Heavy Applications
///usr/bin/env jbang "$0" "$@" ; exit $?
//NATIVE_OPTIONS --no-fallback -H:+ReportExceptionStackTraces
//NATIVE_OPTIONS -H:ReflectionConfigurationFiles=reflection-config.json
// Your reflection-heavy code
JSON Processing
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.fasterxml.jackson.core:jackson-databind:2.15.2
//NATIVE_OPTIONS --no-fallback
//NATIVE_OPTIONS -H:+ReportExceptionStackTraces
import com.fasterxml.jackson.databind.ObjectMapper;
class JsonApp {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
// Your JSON processing code
}
}
Exporting Native Binaries
Container-Based Native Images
Using Docker
Create a Dockerfile
:
FROM ghcr.io/graalvm/graalvm-ce:ol8-java17-22.3.0 AS builder
RUN gu install native-image
COPY . /app
WORKDIR /app
RUN curl -Ls https://sh.jbang.dev | bash -s - app setup
RUN ~/.jbang/bin/jbang --native myapp.java
FROM scratch
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]
Build:
docker build -t myapp-native .
docker run --rm myapp-native arg1 arg2
Multi-Stage Builds
# Builder stage
FROM ghcr.io/graalvm/graalvm-ce:ol8-java17-22.3.0 AS builder
RUN gu install native-image
COPY . /workspace
WORKDIR /workspace
RUN curl -Ls https://sh.jbang.dev | bash -s - --native myapp.java
# Runtime stage
FROM debian:bullseye-slim
COPY --from=builder /workspace/myapp /usr/local/bin/myapp
ENTRYPOINT ["myapp"]
Performance Optimization
Troubleshooting Native Images
Common Issues
Problem: UnsupportedFeatureError
during compilation
Solution: Add reflection configuration or use --no-fallback
to see exact issue
Problem: Missing reflection configuration Solution: Use GraalVM tracing agent:
jbang --jvm=graalvm --runtime-option="-agentlib:native-image-agent=config-output-dir=config" myapp.java
jbang --native --native-option="-H:ConfigurationFileDirectories=config" myapp.java
Problem: Large binary size Solution: Use optimization flags:
//NATIVE_OPTIONS --no-fallback -O2 --gc=serial -H:+StaticExecutableWithDynamicLibC
Problem: Slow compilation Solution: Use parallel compilation:
//NATIVE_OPTIONS -H:+UnlockExperimentalVMOptions -H:+UseParallelGC
Framework-Specific Considerations
Spring Boot
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.springframework.boot:spring-boot-starter-web:3.1.0
//DEPS org.springframework.experimental:spring-native:0.12.1
//NATIVE_OPTIONS --no-fallback --enable-all-security-services
// Spring Boot native configuration
Best Practices
Development Workflow
-
Develop with JVM - faster iteration
-
Test regularly with native - catch issues early
-
Use reflection configuration - for complex apps
-
Profile and optimize - measure before optimizing
Performance Comparison
Metric | JVM | Native Image |
---|---|---|
Startup Time |
~500ms-2s |
~10-50ms |
Memory Usage |
High (heap + metaspace) |
Lower (no JVM overhead) |
Peak Performance |
Higher (JIT optimization) |
Consistent but lower |
Binary Size |
Small JAR + JVM |
Larger standalone binary |
Build Time |
Fast |
Slower (1-10 minutes) |
Use Cases
Perfect for:
-
CLI tools and utilities
-
Serverless functions (AWS Lambda, etc.)
-
Microservices with fast startup requirements
-
Container images with minimal size
-
Desktop applications
Consider alternatives for:
-
Long-running server applications
-
Applications with heavy reflection
-
Development and testing (use JVM for faster iteration)
-
Applications requiring maximum performance
What’s Next?
-
Deploy your native apps → App Installation
-
Learn about containers → Remote Execution
-
Explore performance → Execution Options
-
Try different frameworks → Dependencies
Start building lightning-fast native executables with JBang! ⚡