Building and running a GraalVM native image#
This example shows how to compile Java source code into a native image (executable that does not require the JVM) using GraalVM.
Note that some platform-specific prerequisites must be installed for this to work; see the GraalVM documentation for details.
import cjdk
import subprocess
java_source = """
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
"""
with open("Hello.java", "w") as fp:
fp.write(java_source)
Let’s store the keyword arguments to cjdk.java_env()
so that we can call it
several times with the same configuration.
cjdk_config = dict(vendor="graalvm-java17", version="22.1.0")
The GraalVM native-image
command is not included in the default install, so
we need to use gu
(the GraalVM updater) to install it.
(On macOS, you may see warnings related to setrlimit
in this and following
steps. They can be ignored.)
with cjdk.java_env(**cjdk_config):
subprocess.run(
["gu", "install", "--no-progress", "native-image"], check=True
)
cjdk: Installing JDK graalvm-java17:22.1.0 to /home/runner/.cache/cjdk
Downloading: Component catalog from www.graalvm.org
Processing Component: Native Image
Downloading: Component native-image: Native Image from github.com
Installing new component: Native Image (org.graalvm.native-image, version 22.1.0)
Now let’s compile the source, first with javac
to byte code, then to a native
image.
with cjdk.java_env(**cjdk_config):
subprocess.run(["javac", "Hello.java"], check=True)
subprocess.run(["native-image", "Hello"], check=True)
========================================================================================================================
GraalVM Native Image: Generating 'hello' (executable)...
========================================================================================================================
[1/7] Initializing... (4.4s @ 0.19GB)
Version info: 'GraalVM 22.1.0 Java 17 CE'
C compiler: gcc (linux, x86_64, 11.4.0)
Garbage collector: Serial GC
[2/7] Performing analysis... [******] (13.7s @ 0.60GB)
2,852 (74.19%) of 3,844 classes reachable
3,399 (50.83%) of 6,687 fields reachable
12,932 (44.56%) of 29,020 methods reachable
27 classes, 0 fields, and 345 methods registered for reflection
57 classes, 58 fields, and 51 methods registered for JNI access
[3/7] Building universe... (1.1s @ 0.80GB)
[4/7] Parsing methods... [*] (1.0s @ 1.05GB)
[5/7] Inlining methods... [****] (1.5s @ 0.57GB)
[6/7] Compiling methods... [***] (10.6s @ 1.25GB)
[7/7] Creating image... (1.4s @ 1.56GB)
4.41MB (35.36%) for code area: 7,591 compilation units
6.97MB (55.89%) for image heap: 1,711 classes and 102,361 objects
1.09MB ( 8.75%) for other data
12.47MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 packages in code area: Top 10 object types in image heap:
666.57KB java.util 1014.06KB byte[] for general heap data
338.23KB java.lang 984.94KB java.lang.String
274.64KB java.text 944.53KB byte[] for code metadata
235.70KB java.util.regex 645.59KB java.lang.Class
194.32KB com.oracle.svm.jni 572.10KB byte[] for java.lang.String
193.15KB java.util.concurrent 474.56KB java.util.HashMap$Node
157.78KB com.oracle.svm.core.reflect 222.81KB com.oracle.svm.core.hub.DynamicHubCompanion
147.39KB java.math 201.63KB java.lang.String[]
98.45KB java.util.logging 197.61KB java.util.HashMap$Node[]
94.89KB com.oracle.svm.core.genscavenge 155.91KB java.util.concurrent.ConcurrentHashMap$Node
... 119 additional packages ... 778 additional object types
(use GraalVM Dashboard to see all)
------------------------------------------------------------------------------------------------------------------------
1.6s (4.5% of total time) in 17 GCs | Peak RSS: 3.47GB | CPU load: 3.65
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
/tmp/tmpc80ahs7a/hello (executable)
/tmp/tmpc80ahs7a/hello.build_artifacts.txt
========================================================================================================================
Finished generating 'hello' in 34.8s.
Finally, let’s run the native image. Being a native image, it does not need
java_env()
to run:
r = subprocess.run(["./hello"], check=True)
Hello, World!
r.returncode
0