Skip to content

Commit 2a3f640

Browse files
authored
Merge pull request #355 from graalvm/preserve-package
Add -H:Preserve=package demo
2 parents 4580fb1 + f4c3665 commit 2a3f640

File tree

13 files changed

+921
-0
lines changed

13 files changed

+921
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: native-image/preserve-package
2+
on:
3+
push:
4+
paths:
5+
- 'native-image/preserve-package/**'
6+
- '.github/workflows/native-image-preserve-package.yml'
7+
pull_request:
8+
paths:
9+
- 'native-image/preserve-package/**'
10+
- '.github/workflows/native-image-preserve-package.yml'
11+
schedule:
12+
- cron: "0 0 1 * *" # run every month
13+
workflow_dispatch:
14+
permissions:
15+
contents: read
16+
jobs:
17+
run:
18+
name: Run 'native-image/preserve-package'
19+
runs-on: ubuntu-latest
20+
timeout-minutes: 15
21+
steps:
22+
- uses: actions/checkout@v4
23+
- uses: graalvm/setup-graalvm@v1
24+
with:
25+
java-version: '25-ea'
26+
distribution: 'graalvm'
27+
github-token: ${{ secrets.GITHUB_TOKEN }}
28+
native-image-job-reports: 'true'
29+
- name: Run 'native-image/preserve-package'
30+
run: |
31+
cd native-image/preserve-package
32+
./run.sh

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Demos for building native images, including configurations and setup steps for v
2828
* [list-files](native-image/list-files/) - Shows how to create a native executable from the command line, and then apply Profile-Guided Optimization (PGO)
2929
* [native-build-tools](native-image/native-build-tools/) - Contains two Java projects, and shows how to create native executables from those applications using [Maven](https://graalvm.github.io/native-build-tools/latest/maven-plugin.html) and [Gradle](https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html) plugins for GraalVM Native Image
3030
* [wasm-javac](native-image/wasm-javac/) - Illustrates how to use the new experimental WebAssembly backend in GraalVM to compile `javac` into a Wasm module, which can then run either on the command line or in the browser. [Check out the live demo here](https://graalvm.github.io/graalvm-demos/native-image/wasm-javac/).
31+
* [preserve-package](native-image/preserve-package/) - Demonstrates how to use the `-H:Preserve` option to include all classes from a package in a native image, eliminating the need for JSON metadata configuration.
3132

3233
### Configure
3334
Demos illustrating how to compile applications with Native Image that use some dynamic Java features including reflection, resource access, and so on.

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: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Using Native Image `Preserve` Option
2+
3+
Reflection 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.
4+
GraalVM Native Image automatically supports some uses of reflection.
5+
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/24/docs/api/java.base/java/lang/reflect/package-summary.html).
6+
You must declare any undetected reflection usage to the `native-image` tool, either as metadata ([precomputed in code or as JSON configuration files](https://www.graalvm.org/latest/reference-manual/native-image/metadata/)) or using the `-H:Preserve` option (experimental in GraalVM for JDK 25).
7+
8+
This guide demonstrates how to declare reflection configuration using the `-H:Preserve` option.
9+
10+
## Preparation
11+
12+
1. Download and install the latest GraalVM for JDK 25 (or the early access build before 2025-09-16) using [SDKMAN!](https://sdkman.io/).
13+
14+
```shell
15+
sdk install java 25.ea.29-graal
16+
```
17+
18+
2. Download or clone this repository, and navigate into the `native-image/preserve-package` directory:
19+
20+
```shell
21+
git clone https://github.com/graalvm/graalvm-demos
22+
cd graalvm-demos/native-image/preserve-package
23+
```
24+
25+
## Example Using Reflection on the JVM
26+
27+
The `ReflectionExample` class uses command line argument values to reflectively create an instance of a class and invoke a method with a provided argument. The core code is:
28+
29+
```java
30+
Class<?> clazz = Class.forName(className);
31+
Method method = clazz.getDeclaredMethod(methodName, String.class);
32+
Object result = method.invoke(null, input);
33+
```
34+
35+
This approach works on the JVM as long as the required classes and methods are available on the classpath.
36+
37+
1. Compile the application and create a JAR file using Maven:
38+
39+
```shell
40+
./mvnw package
41+
```
42+
43+
2. Run `ReflectionExample` (the JAR entry point) to invoke the `StringReverser` action:
44+
45+
```shell
46+
$JAVA_HOME/bin/java -jar target/preserve-package-1.0-SNAPSHOT.jar \
47+
org.graalvm.example.action.StringReverser reverse "hello"
48+
```
49+
50+
Expected output:
51+
52+
```shell
53+
olleh
54+
```
55+
56+
3. Run the same command for the `StringCapitalizer` action:
57+
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+
63+
Expected output:
64+
65+
```shell
66+
HELLO
67+
```
68+
69+
## GraalVM Native Image
70+
71+
You can compile the project with Native Image, specifying `ReflectionExample` as the main entry point.
72+
The [_pom.xml_](pom.xml) file uses the [Native Build Tools Maven plugin](https://graalvm.github.io/native-build-tools/latest/maven-plugin.html) to compile the project with the `native-image` tool.
73+
74+
1. Build a native executable using the `native-default` profile (see the [_pom.xml_](pom.xml) file):
75+
76+
```shell
77+
./mvnw package -Pnative-default
78+
```
79+
80+
2. Run the resulting `example-default` native executable:
81+
82+
```bash
83+
./target/example-default \
84+
org.graalvm.example.action.StringReverser reverse "hello"
85+
```
86+
87+
You will see a `ClassNotFoundException` similar to:
88+
89+
```shell
90+
Exception in thread "main" java.lang.ClassNotFoundException: org.graalvm.example.action.StringReverser
91+
at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:339)
92+
at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:298)
93+
at java.base@25/java.lang.Class.forName(DynamicHub.java:1758)
94+
at java.base@25/java.lang.Class.forName(DynamicHub.java:1704)
95+
at java.base@25/java.lang.Class.forName(DynamicHub.java:1691)
96+
at org.graalvm.example.ReflectionExample.main(ReflectionExample.java:56)
97+
at java.base@25/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
98+
```
99+
100+
This error occurs because the `native-image` tool's static analysis did not determine that your application uses the `StringReverser` class, and did not include it in the native executable.
101+
102+
## Native Image Using `-H:Preserve`
103+
104+
GraalVM for JDK 25 introduces the `-H:Preserve` option.
105+
106+
This option lets you instruct the `native-image` tool to keep entire packages, modules, or all classes on the classpath.
107+
108+
<!-- This can result in larger application size. -->
109+
110+
In this example, both classes used via reflection are in the `org.graalvm.example.action` package.
111+
You can use `-H:Preserve=package` to keep all of the classes in that package in the native executable, even if static analysis cannot discover them.
112+
113+
Native Image command line arguments can be specified as `<buildArgs>` in the `native-maven-plugin` configuration.
114+
Since the `-H:Preserve` option is experimental, you must also enable its use with `-H:+UnlockExperimentalVMOptions`.
115+
For the complete plugin configuration, see the [_pom.xml_](pom.xml) file:
116+
117+
```xml
118+
<configuration>
119+
...
120+
<buildArgs>
121+
<buildArg>-H:+UnlockExperimentalVMOptions</buildArg>
122+
<buildArg>-H:Preserve=package=org.graalvm.example.action</buildArg>
123+
</buildArgs>
124+
</configuration>
125+
```
126+
127+
1. Build a native executable using the `native-preserve` profile, which adds `-H:Preserve=package=org.graalvm.example.action` when running the `native-image` tool:
128+
129+
```shell
130+
./mvnw package -Pnative-preserve
131+
```
132+
133+
2. Run the new `example-preserve` executable to confirm the previously missing `StringReverser` class and its methods are now included:
134+
135+
```shell
136+
./target/example-preserve \
137+
org.graalvm.example.action.StringReverser reverse "hello"
138+
```
139+
140+
Expected output:
141+
142+
```shell
143+
olleh
144+
```
145+
146+
3. Run the executable to confirm the `StringCapitalizer` class works too:
147+
148+
```shell
149+
./target/example-preserve \
150+
org.graalvm.example.action.StringCapitalizer capitalize "hello"
151+
```
152+
153+
Expected output:
154+
155+
```shell
156+
HELLO
157+
```
158+
159+
As demonstrated, `-H:Preserve` provides an easy way to ensure that Native Image includes classes not discovered by static analysis.
160+
161+
### Related Documentation
162+
163+
* [Reachability Metadata: Reflection](https://www.graalvm.org/latest/reference-manual/native-image/metadata/)
164+
* [Assisted Configuration with Tracing Agent](https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/#tracing-agent)
165+
* [Java Reflection API](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/package-summary.html)

0 commit comments

Comments
 (0)