Skip to content

Commit 0c6ff15

Browse files
committed
Add -H:Preserve=package demo
1 parent 29c0e23 commit 0c6ff15

File tree

10 files changed

+868
-0
lines changed

10 files changed

+868
-0
lines changed

native-image/preserve-package/.mvn/jvm.config

Whitespace-only changes.

native-image/preserve-package/.mvn/maven.config

Whitespace-only changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
wrapperVersion=3.3.2
18+
distributionType=only-script
19+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# Using Native Image `Preserve` Option
2+
3+
[Reflection](https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/reflect/package-summary.html) is a feature of the Java programming language that enables a running Java program to examine and modify attributes of its classes, interfaces, fields, and methods and GraalVM Native Image provides automatic support for some uses. Native Image uses static analysis to identify what classes, methods, and fields are needed by an application but it may not detect some elements of your application that are accessed using the [Java Reflection API](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/package-summary.html). Undetected Reflection usage must be declared to the `native-image` tool either in the form of metadata (precomputed in code or as JSON configuration files) or using the `-H:Preserve` option (experimental in GraalVM for JDK 25).
4+
5+
The following demonstrates how to declare Reflection configuration using the `-H:Preserve` option.
6+
7+
## Preparation
8+
9+
1. Download and install the latest GraalVM for JDK 25 (or early access build before 2025-09-16) using [SDKMAN!](https://sdkman.io/).
10+
11+
```shell
12+
sdk install java 25.ea.29-graal
13+
```
14+
15+
2. Download or clone this repository and navigate into the `native-image/preserve-package` directory:
16+
17+
```shell
18+
git clone https://github.com/graalvm/graalvm-demos
19+
```
20+
21+
```shell
22+
cd graalvm-demos/native-image/preserve-package
23+
```
24+
25+
## Example using Reflection on the JVM
26+
27+
The `ReflectionExample` class will use command line argument values to
28+
reflectively create an instance of a class and invoke a method with a
29+
provided argument. The core code is:
30+
31+
```java
32+
Class<?> clazz = Class.forName(className);
33+
Method method = clazz.getDeclaredMethod(methodName, String.class);
34+
Object result = method.invoke(null, input);
35+
```
36+
37+
This works fine when running the JVM and the named classes and methods are on
38+
the application classpath.
39+
40+
1. Compile the application and create a jar using Maven:
41+
```shell
42+
./mvnw package
43+
```
44+
2. Run `ReflectionExample` (the jar entry point) and instruct it to invoke the
45+
`StringReverser` action:
46+
47+
```shell
48+
$JAVA_HOME/bin/java -jar target/preserve-package-1.0-SNAPSHOT.jar \
49+
org.graalvm.example.action.StringReverser reverse "hello"
50+
```
51+
52+
Expected output:
53+
```shell
54+
olleh
55+
```
56+
57+
3. Do the same for the `StringCapitalizer` action:
58+
```shell
59+
$JAVA_HOME/bin/java -jar target/preserve-package-1.0-SNAPSHOT.jar \
60+
org.graalvm.example.action.StringCapitalizer capitalize "hello"
61+
```
62+
Expected output:
63+
```shell
64+
HELLO
65+
```
66+
67+
## GraalVM Native Image
68+
69+
We can compile with Native Image specifying the `ReflectionExample` as the main
70+
entry point. The project [`pom.xml`](pom.xml) uses the [GraalVM Native Build
71+
Tools](https://graalvm.github.io/native-build-tools/latest/index.html) plugin to
72+
compile the project using the `native-image` tool when the `native-default`
73+
profile is specified.
74+
75+
1. Build a native executable using the `native-default` profile (see [`pom.xml`](pom.xml)):
76+
```shell
77+
./mvnw package -Pnative-default
78+
```
79+
4. Run the resulting `example-default` native executable, using the following command:
80+
```bash
81+
./target/example-default \
82+
org.graalvm.example.action.StringReverser reverse "hello"
83+
```
84+
You will see a `ClassNotFoundException` exception, similar to:
85+
```shell
86+
Exception in thread "main" java.lang.ClassNotFoundException: org.graalvm.example.action.StringReverser
87+
at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:339)
88+
at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:298)
89+
at java.base@25/java.lang.Class.forName(DynamicHub.java:1758)
90+
at java.base@25/java.lang.Class.forName(DynamicHub.java:1704)
91+
at java.base@25/java.lang.Class.forName(DynamicHub.java:1691)
92+
at org.graalvm.example.ReflectionExample.main(ReflectionExample.java:56)
93+
at java.base@25/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
94+
```
95+
What happened!? Based on its static analysis, the `native-image` tool was unable
96+
to determine that class `StringReverser` is used by the application and
97+
therefore did not include it in the native executable.
98+
99+
## Native Image using -H:Preserve
100+
101+
New in GraalVM for JDK 25 is the `-H:Preserve` option which makes it easy to instruct the
102+
`native-image` tool to preserve (i.e., keep entirely) packages, modules, and
103+
even all classes on the classpath (which can result in very large applications).
104+
105+
Conveniently in this example, both of the classes that are being used via
106+
reflection are in the `org.graalvm.example.action` package. We can use
107+
`-H:Preserve=package` to keep all of the classes in that package in the native
108+
executable, even though their use is not discoverable through static analysis.
109+
110+
Native Image command line arguments can be specified as `<buildArgs>` in the
111+
`native-maven-plugin` configuration. Note that since the `-H:Preserve` option
112+
is new and considered experimental in GraalVM for JDK 25, you must also enable
113+
its use with `-H:+UnlockExperimentalVMOptions`. See the [`pom.xml`](pom.xml) for
114+
the complete plugin configuration:
115+
116+
```xml
117+
<configuration>
118+
...
119+
<buildArgs>
120+
<buildArg>-H:+UnlockExperimentalVMOptions</buildArg>
121+
<buildArg>-H:Preserve=package=org.graalvm.example.action</buildArg>
122+
</buildArgs>
123+
</configuration>
124+
```
125+
126+
1. Build a native executable using the `native-preserve` profile which adds
127+
`-H:Preserve=package=org.graalvm.example.action` when running the `native-image`
128+
tool (see [`pom.xml`](pom.xml)):
129+
```shell
130+
./mvnw package -Pnative-preserve
131+
```
132+
133+
2. Run the new `example-preserve` executable to confirm the previously missing
134+
`StringReverser` class and all its methods are now included:
135+
```shell
136+
./target/example-preserve \
137+
org.graalvm.example.action.StringReverser reverse "hello"
138+
```
139+
The expected "olleh" output should be the result, just like on the JVM.
140+
141+
3. Invoke the `StringCapitalizer` to see if that now works too:
142+
143+
```shell
144+
./target/example-preserve \
145+
org.graalvm.example.action.StringCapitalizer capitalize "hello"
146+
```
147+
The expected "HELLO" output should be the result.
148+
149+
150+
As demonstrated, `-H:Preserve` provides an easy way to ensure classes not
151+
discovered by GraalVM Native Image's static analysis are included in an
152+
executable.
153+
154+
### Related Documentation
155+
156+
* [Reachability Metadata: Reflection](https://www.graalvm.org/latest/reference-manual/native-image/metadata/)
157+
* [Assisted Configuration with Tracing Agent](https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/#tracing-agent)
158+
* [java.lang.reflect Javadoc](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/package-summary.html)

0 commit comments

Comments
 (0)