Compiling the application to a native executable
Building a native executable requires using a distribution of GraalVM.
GraalVM
https://www.graalvm.org/latest/docs/introduction/
GraalVM for JDK 21 is required. https://www.graalvm.org/downloads/
Native Image
GraalVM compiles your Java applications ahead of time into standalone binaries. These binaries are smaller, start up to 100x faster, provide peak performance with no warmup, and use less memory and CPU than applications running on a Java Virtual Machine (JVM).
GraalVM reduces the attack surface of your application. It excludes unused classes, methods, and fields from the application binary. It restricts reflection and other dynamic Java language features to build time only. It does not load any unknown code at run time.
Popular microservices frameworks such as Spring Boot, Micronaut, Helidon, and Quarkus, and cloud platforms such as Oracle Cloud Infrastructure, Amazon Web Services, Google Cloud Platform, and Microsoft Azure all support GraalVM.
With profile-guided optimization and the G1 (Garbage-First) garbage collector, you can get lower latency and on-par or better peak performance and throughput compared to applications running on a Java Virtual Machine (JVM).
You can use the GraalVM JDK just like any other Java Development Kit in your IDE.
Native Image is a technology to compile Java code ahead-of-time to a binary – a native executable. A native executable includes only the code required at run time, that is the application classes, standard-library classes, the language runtime, and statically-linked native code from the JDK.
An executable file produced by Native Image has several important advantages, in that it
- Uses a fraction of the resources required by the Java Virtual Machine, so is cheaper to run
- Starts in milliseconds
- Delivers peak performance immediately, with no warmup
- Can be packaged into a lightweight container image for fast and efficient deployment
- Presents a reduced attack surface
A native executable is created by the Native Image builder or native-image that processes your application classes and other metadata to create a binary for a specific operating system and architecture. First, the native-image tool performs static analysis of your code to determine the classes and methods that are reachable when your application runs. Second, it compiles classes, methods, and resources into a binary. This entire process is called build time to clearly distinguish it from the compilation of Java source code to bytecode.
The native-image tool can be used to build a native executable, which is the default, or a native shared library. This quick start guide focuses on building a native executable; to learn more about native shared libraries, go here.
To get used to Native Image terminology and get better understanding of the technology, we recommend you to read the Basics of Native Image.
Build a Native Executable
- On Linux, you will need GCC, and the glibc and zlib headers. Examples for common distributions:
# dnf (rpm-based) sudo dnf install gcc glibc-devel zlib-devel libstdc++-static # Debian-based distributions: sudo apt-get install build-essential libz-dev zlib1g-devXCode provides the required dependencies on macOS:3
xcode-select --installOn Windows, you will need to install the Visual Studio 2017 Visual C++ Build Tools
- sudo apt-get install build-essential libz-dev zlib1g-dev (example in Ubuntu.)
- HelloWorld.java:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, Native World!");
}
}- Compile it and build a native executable from the Java class:
javac HelloWorld.java
native-image HelloWorldOutput:
native-image HelloWorld
========================================================================================================================
GraalVM Native Image: Generating 'helloworld' (executable)...
========================================================================================================================
[1/8] Initializing... (4.7s @ 0.07GB)
Java version: 21.0.1+12, vendor version: Oracle GraalVM 21.0.1+12.1
Graal compiler: optimization level: 2, target machine: x86-64-v3, PGO: ML-inferred
C compiler: gcc (linux, x86_64, 11.4.0)
Garbage collector: Serial GC (max heap size: 80% of RAM)
1 user-specific feature(s):
- com.oracle.svm.thirdparty.gson.GsonFeature
------------------------------------------------------------------------------------------------------------------------
Build resources:
- 11.67GB of memory (75.6% of 15.45GB system memory, determined at start)
- 8 thread(s) (100.0% of 8 available processor(s), determined at start)
[2/8] Performing analysis... [*****] (10.6s @ 0.19GB)
2,164 reachable types (61.4% of 3,522 total)
2,100 reachable fields (46.2% of 4,546 total)
10,025 reachable methods (38.7% of 25,914 total)
758 types, 112 fields, and 476 methods registered for reflection
49 types, 32 fields, and 48 methods registered for JNI access
4 native libraries: dl, pthread, rt, z
[3/8] Building universe... (1.7s @ 0.20GB)
[4/8] Parsing methods... [**] (4.0s @ 0.26GB)
[5/8] Inlining methods... [***] (1.2s @ 0.28GB)
[6/8] Compiling methods... [*****] (24.0s @ 0.28GB)
[7/8] Layouting methods... [*] (1.4s @ 0.37GB)
[8/8] Creating image... [*] (2.0s @ 0.27GB)
3.56MB (45.54%) for code area: 4,750 compilation units
3.83MB (49.00%) for image heap: 58,434 objects and 43 resources
436.27kB ( 5.45%) for other data
7.81MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area: Top 10 object types in image heap:
2.00MB java.base 939.70kB byte[] for code metadata
1.26MB svm.jar (Native Image) 736.07kB byte[] for java.lang.String
88.85kB com.oracle.svm.svm_enterprise 391.31kB java.lang.String
41.55kB jdk.proxy3 360.02kB heap alignment
39.62kB jdk.proxy1 344.02kB java.lang.Class
29.92kB org.graalvm.nativeimage.base 157.38kB java.util.HashMap$Node
27.71kB org.graalvm.collections 114.01kB char[]
21.34kB jdk.internal.vm.ci 104.02kB byte[] for reflection metadata
16.95kB jdk.internal.vm.compiler 91.62kB java.lang.Object[]
11.68kB jdk.proxy2 84.53kB com.oracle.svm.core.hub.DynamicHubCompanion
389.00B for 1 more packages 597.33kB for 567 more object types
Use '-H:+BuildReport' to create a report with more details.
------------------------------------------------------------------------------------------------------------------------
Security report:
- Binary does not include Java deserialization.
- Use '--enable-sbom' to embed a Software Bill of Materials (SBOM) in the binary.
------------------------------------------------------------------------------------------------------------------------
Recommendations:
G1GC: Use the G1 GC ('--gc=G1') for improved latency and throughput.
PGO: Use Profile-Guided Optimizations ('--pgo') for improved throughput.
INIT: Adopt '--strict-image-heap' to prepare for the next GraalVM release.
HEAP: Set max heap for improved and more predictable memory usage.
CPU: Enable more CPU features with '-march=native' for improved performance.
------------------------------------------------------------------------------------------------------------------------
2.5s (5.0% of total time) in 170 GCs | Peak RSS: 0.90GB | CPU load: 5.82
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
/media/zbyszek/Nowy/zjcCV/jreact.com/tmp/helloworld (executable)
========================================================================================================================
Finished generating 'helloworld' in 50.4s.
- Run the application: ./helloworld
Hello, Native World!- Project directory:
tmp/helloworld
tmp/HelloWorld.class
tmp/HelloWorld.java