How to Prepare Native Image Configuration Files in The Most Complete Format?
GraalVM is a high-performance runtime that supports additional programming languages and execution modes but the main thing that makes it popular is a technology to
ahead-of-time compile Java code to a standalone executable called
Why are Native Image Configuration Files Needed?
As every rose has it’s a thorn, although the
native image is an exciting technology, it has some limitations due to the nature of
ahead-of-time compilation. The
native image builder statically analyzes all classes of the application and their dependencies to determine which classes and methods are reachable during the application execution. This is an optimization process and it's executed according to the
closed-world assumption that all application classes need to be known at
native image generation time so that the static analysis can process them.
On the other hand, in the
open-world approach, Java developers can look up classes, methods, and fields with their names and access/invoke them by
Java Reflection. At this point, a clear conflict arises between the
closed-world assumption and the
open-world approach. However, the
native image builder tries to resolve the target elements in the static analysis time mentioned above to detects calls to the
Reflection API but in all cases, it cannot be successful.
This is the reason
GraalVM needs configuration files to use at
native image build time for features that follow the
open-world approach, such as
Dynamic Class Loading,
It could be so easy to prepare the configuration files from scratch for the above-mentioned features when we do not use 3rd party libraries in our application. However, it is an undeniable fact that we needed and used 3rd party libraries widely in real-world applications.
Let’s think about the difficulty of manual providing configuration files from scratch when using 3rd party libraries with an example. Suppose we have an application that consumes a
Kafka topic. The application uses explicitly some classes of the
Kafka client depending on record types due to
Deserializing need and we know these classes must be defined in the configuration file for the
native image generation process. However, other classes can also be accessed via reflection by the client under the hood for consumption and the same need. It is not easy to detect these classes always that are used implicitly from our point of view and this difficulty makes manual preparing the configuration troublesome and error-prone when you want to generate a
native image from the application.
Fortunately, thanks to the
tracing agent, preparing these configuration files becomes easier and more convenient. The agent tracks all usages of dynamic features of execution on a regular Java VM and creates configuration files in the directory you defined at the command-line.
How Can I Use?
You can enable the agent on the command line of the
GraalVM java command.
$GRAAL_HOME/bin/java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/ -jar /path/to/jar-dir/
Notice that, -agentlib option must be specified before the -jar option(similarly before the class name) and any other application parameters in the java command line.
During execution, the agent intercepts all calls related to the limitations mentioned earlier, and after the JVM process terminates, it generates the configuration files based on the calls it interrupts in the specified output directory. You will have the following files after execution.
These files are standalone configuration files in JSON format which contain all intercepted dynamic accesses. For our hypothetical example mentioned above, the contents of the
reflect-config.json file would be as follows.
Lines unrelated to our 3rd party library have been omitted to reduce the length of the file.
In most cases, to get the most better coverage of dynamic accesses, you want to run the target application more than once with different inputs to trigger separate execution paths. In this case, you can use
config-merge-dir option which augments the configuration files by adding the intercepted accesses to existing files.
Manually Inspection The Configuration Files
The developers of
GraalVM suggest manual inspection of the generated configuration files because the agent observes only code that was executed. Depending on your usage scenario, it's always possible was missed some elements in the resulting configuration files. In addition, the agent isn't perfect, so it can generate non-compatible entries with the runtime environment hence you shouldn't hesitate to make changes manually on the files when the need arises.
GraalVM executes the native image build process according to the
closed-world assumption that means all application classes need to be known at
native image generation time. Hence, it needs configuration files to use at native image build time for supporting some features that follow the
open-world approach, such as
Dynamic Class Loading,
It is difficult and error-prone to manual provide configuration files from scratch when using 3rd party libraries. The
tracing agent is a facilitating solution at this point. It intercepts all dynamic calls and then generates the configuration files based on the calls. Because the agent observes only code that was executed, it is most important to run the target application more than once with different inputs to trigger separate execution paths for getting better coverage of dynamic accesses.