|
1 | 1 | --- |
2 | 2 | layout: ni-docs |
3 | 3 | toc_group: how-to-guides |
4 | | -link_title: Specify Class Initialization |
| 4 | +link_title: Specify Class Initialization Explicitly |
5 | 5 | permalink: /reference-manual/native-image/guides/specify-class-initialization/ |
6 | 6 | --- |
7 | 7 |
|
8 | 8 | # Specify Class Initialization Explicitly |
9 | 9 |
|
10 | | -Two command line flags explicitly specify when a class should be initialized: `--initialize-at-build-time` and `--initialize-at-run-time`. |
11 | | -You can use the flags to specify whole packages or individual classes. |
12 | | -For example, if you have the classes `p.C1`, `p.C2`, … ,`p.Cn`, you can specify that all the classes in the package `p` are initialized at build time by passing the following argument to `native-image` on the command line: |
| 10 | +By default, Native Image initializes application classes at runtime, except for the classes that Native Image proves "safe" for initialization at build time. |
| 11 | +However, you can influence the default behavior by explicitly specifying the classes to be initialized at build-time or runtime. |
| 12 | +For that, there are two command-line options: `--initialize-at-build-time` and `--initialize-at-run-time`. |
| 13 | +You can use these options to specify whole packages or individual classes. |
| 14 | +For example, if you have the classes `p.C1`, `p.C2`, … ,`p.Cn`, you can specify that all the classes in the package `p` are to be initialized at build time by passing the following option to `native-image`: |
13 | 15 | ```shell |
14 | 16 | --initialize-at-build-time=p |
15 | 17 | ``` |
16 | | - |
17 | | -If you want only one of the classes in package `p` to be initialized at runtime, use: |
| 18 | +If you want only class `C1` in package `p` to be initialized at runtime, use: |
18 | 19 | ```shell |
19 | 20 | --initialize-at-run-time=p.C1 |
20 | 21 | ``` |
21 | 22 |
|
22 | | -The whole class hierarchy can be initialized at build time by passing `--initialize-at-build-time` on the command line. |
| 23 | +You can also programmatically specify class initialization using the [`RuntimeClassInitialization`] class (https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeClassInitialization.java) from the [Native Image Feature interface](https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java). |
| 24 | + |
| 25 | +This guide demonstrates how to build a native executable by running the class initializer at runtime (default behavior), and then at build time, and compares the two approaches. |
| 26 | + |
| 27 | +### Prerequisite |
| 28 | +Make sure you have installed a GraalVM JDK. |
| 29 | +The easiest way to get started is with [SDKMAN!](https://sdkman.io/jdks#graal). |
| 30 | +For other installation options, visit the [Downloads](https://www.graalvm.org/downloads/) section. |
| 31 | + |
| 32 | +## Run a Demo |
| 33 | + |
| 34 | +For the demo, run a simple Java application that parses some Java talks from 2023. |
| 35 | +The parser creates records and adds them to a `List<Talk>` collection. |
| 36 | + |
| 37 | +1. Save the following Java source code in a file named _TalkParser.java_: |
| 38 | + ```java |
| 39 | + import java.util.ArrayList; |
| 40 | + import java.util.List; |
| 41 | + import java.util.Scanner; |
| 42 | + |
| 43 | + public class TalkParser { |
| 44 | + private static final List<Talk> TALKS = new ArrayList<>(); |
| 45 | + static { |
| 46 | + Scanner s = new Scanner(""" |
| 47 | + Asynchronous Programming in Java: Options to Choose from by Venkat Subramaniam |
| 48 | + Anatomy of a Spring Boot App with Clean Architecture by Steve Pember |
| 49 | + Java in the Cloud with GraalVM by Alina Yurenko |
| 50 | + Bootiful Spring Boot 3 by Josh Long |
| 51 | + """); |
| 52 | + while (s.hasNextLine()) { |
| 53 | + TALKS.add(new Talk(s.nextLine())); |
| 54 | + } |
| 55 | + s.close(); |
| 56 | + } |
| 57 | + |
| 58 | + public static void main(String[] args) { |
| 59 | + System.out.println("Talks loaded using scanner:"); |
| 60 | + for (Talk talk : TALKS) { |
| 61 | + System.out.println("- " + talk.name()); |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + record Talk (String name) {} |
| 67 | + ``` |
| 68 | + |
| 69 | +2. Compile the application: |
| 70 | + ```bash |
| 71 | + javac TalkParser.java |
| 72 | + ``` |
| 73 | + |
| 74 | +3. Build a native executable, explicitly running the class initializer at runtime: |
| 75 | + ```bash |
| 76 | + native-image --initialize-at-run-time=TalkParser,Talk -o runtime-parser TalkParser |
| 77 | + ``` |
| 78 | + You can omit the `--initialize-at-run-time=TalkParser,Talk` option in this example because these classes are marked for initialization at run time by default. |
| 79 | + The `-o` option specifies the name of the output file. |
| 80 | + |
| 81 | +4. Run and `time` the native application: |
| 82 | + ```bash |
| 83 | + time ./runtime-parser |
| 84 | + ``` |
| 85 | + On a machine with 16 GB of memory and 8 cores, you should see a result similar to: |
| 86 | + ``` |
| 87 | + Talks loaded using scanner: |
| 88 | + - Asynchronous Programming in Java: Options to Choose from by Venkat Subramaniam |
| 89 | + - Anatomy of a Spring Boot App with Clean Architecture by Steve Pember |
| 90 | + - Java in the Cloud with GraalVM by Alina Yurenko |
| 91 | + - Bootiful Spring Boot 3 by Josh Long |
| 92 | + ./runtime-parser 0.00s user 0.00s system 52% cpu 0.010 total |
| 93 | + ``` |
| 94 | + The application parses the text block at runtime. |
| 95 | + |
| 96 | + Check the file size which should be around 13M: |
| 97 | + ``` |
| 98 | + du -sh runtime-parser |
| 99 | + ``` |
| 100 | + |
| 101 | +5. Next, build a native executable initializing `TalkParser` at build time, and providing a different name for the output file to differentiate it from the previous build. The `Talk` record has to be initialized explicitly too, so the objects of this type will be persisted in the executable heap. |
| 102 | + ```bash |
| 103 | + native-image --initialize-at-build-time=TalkParser,Talk -o buildtime-parser TalkParser |
| 104 | + ``` |
| 105 | + |
| 106 | + If your application adds additional types to the executable heap, each type (or the corresponding package) needs to be marked for build-time initialization explicitly to fulfill the requirements of `--strict-image-heap`. |
| 107 | + An appropriate actionable error message will guide you through the process. |
| 108 | + |
| 109 | +6. Run and `time` the second executable for comparison: |
| 110 | + ```bash |
| 111 | + time ./buildtime-parser |
| 112 | + ``` |
| 113 | + This time you should see something similar to this: |
| 114 | + ``` |
| 115 | + Talks loaded using scanner: |
| 116 | + - Asynchronous Programming in Java: Options to Choose from by Venkat Subramaniam |
| 117 | + - Anatomy of a Spring Boot App with Clean Architecture by Steve Pember |
| 118 | + - Java in the Cloud with GraalVM by Alina Yurenko |
| 119 | + - Bootiful Spring Boot 3 by Josh Long |
| 120 | + ./buildtime-parser 0.00s user 0.00s system 53% cpu 0.016 total |
| 121 | + ``` |
| 122 | + Check the file size which should decrease to around 6.4M! |
| 123 | + ``` |
| 124 | + du -sh buildtime-parser |
| 125 | + ``` |
| 126 | + The file size change is because Native Image runs the static initializer at build time, parsing the text block, and persisting only the `Talk` records in the executable. |
| 127 | + As a result, the majority of the scanning infrastructure does not become reachable when Native Image statically analyzes the application and is, therefore, not included in the executable. |
| 128 | + |
| 129 | +Another valuable criterion for profiling applications more accurately is the number of instructions, which can be obtained using the [Linux `perf` profiler](../PerfProfiling.md). |
| 130 | + |
| 131 | +For example, for this demo application, the number of instructions decreased by almost 30% (from 11.8M to 8.6M) in the case of build-time class initialization: |
| 132 | +```bash |
| 133 | +perf stat ./runtime-parser |
| 134 | +Talks loaded using scanner: |
| 135 | +- Asynchronous Programming in Java: Options to Choose from by Venkat Subramaniam |
| 136 | +(...) |
| 137 | + Performance counter stats for './runtime-parser': |
| 138 | +(...) |
| 139 | + 11,323,415 cycles # 3.252 GHz |
| 140 | + 11,781,338 instructions # 1.04 insn per cycle |
| 141 | + 2,264,670 branches # 650.307 M/sec |
| 142 | + 28,583 branch-misses # 1.26% of all branches |
| 143 | +(...) |
| 144 | + 0.003817438 seconds time elapsed |
| 145 | + 0.000000000 seconds user |
| 146 | + 0.003878000 seconds sys |
| 147 | +``` |
| 148 | +```bash |
| 149 | +perf stat ./buildtime-parser |
| 150 | +Talks loaded using scanner: |
| 151 | +- Asynchronous Programming in Java: Options to Choose from by Venkat Subramaniam |
| 152 | +(...) |
| 153 | + Performance counter stats for './buildtime-parser': |
| 154 | +(...) |
| 155 | + 9,534,318 cycles # 3.870 GHz |
| 156 | + 8,609,249 instructions # 0.90 insn per cycle |
| 157 | + 1,640,540 branches # 665.818 M/sec |
| 158 | + 23,490 branch-misses # 1.43% of all branches |
| 159 | +(...) |
| 160 | + 0.003119519 seconds time elapsed |
| 161 | + 0.001113000 seconds user |
| 162 | + 0.002226000 seconds sys |
| 163 | +``` |
| 164 | + |
| 165 | +This demonstrates how Native Image can shift work from runtime to build time: when the class is initialized at build time, the text block is parsed when the executable is being built and only the parsed objects are included. |
| 166 | +This not only makes the executable smaller in file size, but also faster to run: when the executable runs, the `Talk` records already exist and only need to be printed. |
| 167 | + |
| 168 | +To ensure native executables built with Native Image are as compatible as possible with the HotSpot behavior, application classes that cannot be safely initialized at build time, are initialized at runtime. |
| 169 | +You as a user, or a framework that you use, must explicitly request build-time initialization for certain classes to benefit from smaller file sizes and faster times to run. |
| 170 | +Include the right data structures to avoid the image size blowing up instead. |
| 171 | +We also recommend using `--initialize-at-build-time` with single classes only. |
| 172 | +It may be that you need to add a lot of `--initialize-at-build-time` entries. |
| 173 | +Note that incorrect build-time initialization can lead to problems that are to be avoided in production settings such as dysfunctional behavior or including sensitive data such as passwords or encryption keys. |
| 174 | + |
| 175 | +### Conclusion |
23 | 176 |
|
24 | | -Class initialization can also be specified programmatically using [`RuntimeClassInitialization`](https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeClassInitialization.java) from the [Native Image feature](https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java). |
| 177 | +This guide demonstrated how you can influence the default `native-image` class initialization policy, and configure it to initialize a specific class at build time, depending on the use case. |
| 178 | +The benefits of the build-time versus run-time initialization are described in [Class Initialization in Native Image](../ClassInitialization.md), but, in short, build-time initialization can significantly decrease the overall file size and improve the runtime of your application when used correctly. |
25 | 179 |
|
26 | 180 | ### Related Documentation |
27 | 181 |
|
28 | 182 | * [Class Initialization](../ClassInitialization.md) |
29 | | -* [Native Image Build Configuration](../BuildConfiguration.md) |
30 | | -* [Use System Properties in a Native Executable](use-system-properties.md) |
| 183 | +* [Native Image Build Configuration](../BuildConfiguration.md) |
0 commit comments