docs/reference-manual/native-image/ReachabilityMetadata.md
The dynamic language features of the JVM (for example, reflection and resource handling) compute the dynamically-accessed program elements such as fields, methods, or resource URLs at run time. On HotSpot this is possible because all class files and resources are available at run time and can be loaded by the runtime. Availability of all classes and resources, and their loading at run time, comes with an extra overhead in memory and startup time.
To make native binaries small, the native-image builder performs static analysis at build time to determine only the necessary program elements that are needed for the correctness of the application.
Small binaries allow fast application startup and low memory footprint, however they come at a cost: determining dynamically-accessed application elements via static analysis is infeasible as reachability of those elements depends on data that is available only at run time.
To ensure inclusion of necessary dynamically-accessed elements into the native binary, the native-image builder requires reachability metadata (hereinafter referred to as metadata).
Providing the builder with correct and exhaustive reachability metadata guarantees application correctness and ensures compatibility with third-party libraries at runtime.
You can provide reachability metadata to the native-image builder using the following methods:
-H:Preserve=<classpath-selector> flag. For detailed instructions, please refer to the -H:Preserve= documentation.Note: Native Image is migrating to the more user-friendly implementation of reachability metadata that shows problems early on and allows easy debugging.
To enable the new user-friendly reachability-metadata mode for your application, pass the option
--exact-reachability-metadataat build time. To enable the user-friendly mode only for concrete packages, pass--exact-reachability-metadata=<comma-separated-list-of-packages>.To get an overview of all places in your code where missing registrations occur, without committing to the exact behavior, you can pass
-XX:MissingRegistrationReportingMode=Warnwhen starting the application.To detect places where the application accidentally ignores a missing registration error (with
catch (Throwable t)blocks), pass-XX:MissingRegistrationReportingMode=Exitwhen starting the application. The application will then unconditionally print the error message with the stack trace and exit immediately. This behavior is ideal for running application tests to guarantee all metadata is included.The user-friendly implementation for reflection will become the default in future releases of GraalVM so the timely adoption is important to avoid project breakage.
Computing metadata in code can be achieved in two ways:
By providing constant arguments to functions that dynamically access elements of the JVM. See, for example, Class#forName in the following code:
class ReflectiveAccess {
public Class<Foo> fetchFoo() throws ClassNotFoundException {
return Class.forName("Foo");
}
}
Here, Class.forName("Foo") is evaluated into a constant at build time. When the native binary is built, this value is stored in its initial heap.
If the class Foo does not exist, the call to Class#forName will be transformed into throw ClassNotFoundException("Foo").
The constant is defined as:
"Foo" or 1)."F" + "oo", or an indexing into an array).When passing constant arrays, the following approaches to declare and populate an array are equivalent from the point of view of the native-image builder:
Class<?>[] params0 = new Class<?>[]{String.class, int.class};
Integer.class.getMethod("parseInt", params0);
Class<?>[] params1 = new Class<?>[2];
params1[0] = Class.forName("java.lang.String");
params1[1] = int.class;
Integer.class.getMethod("parseInt", params1);
Class<?>[] params2 = {String.class, int.class};
Integer.class.getMethod("parseInt", params2);
Note that Native Image currently aggressively computes constants, and therefore it is not possible to specify exactly what is a constant at build time.
By initializing classes at build time and storing dynamically accessed elements into the initial heap of the native executable. This way of providing metadata is suited for cases when specifying metadata with constants or in JSON is not possible. This is necessary in cases when:
In the following example:
class InitializedAtBuildTime {
private static Class<?> aClass;
static {
try {
aClass = Class.forName(readFile("class.txt"));
} catch (ClassNotFoundException e) {
throw RuntimeException(e);
}
}
public Class<?> fetchFoo() {
return aClass;
}
}
The dynamically accessed elements will be included into the native executable's heap only if that part of the heap is reachable through an enclosing method (for example, InitializedAtBuildTime#fetchFoo) or a static field (for example, InitializedAtBuildTime.aClass).
All metadata specified in the reachability-metadata.json file that is located in any of the classpath entries at META-INF/native-image/<group.Id>/<artifactId>/. The JSON schema for the reachability metadata is defined in reachability-metadata-schema-v1.2.0.json.
A sample reachability-metadata.json file can be found in the sample section. The reachability-metadata.json configuration contains a single object with one field for each type of metadata. Each field in the top-level object contains an array of metadata entries:
{
"reflection":[],
"resources":[]
}
For example, Java reflection metadata is specified under reflection, and an example entry looks like:
{
"reflection": [
{
"type": "Foo"
}
]
}
Each entry in JSON-based metadata should be conditional to avoid unnecessary growth of the native binary size.
A conditional entry is specified by adding a condition field to the entry in the following way:
{
"condition": {
"typeReached": "<fully-qualified-class-name>"
},
<metadata-entry>
}
A metadata entry with a typeReached condition is considered available at run time, only when the specified fully-qualified type is reached at run time.
Before that, all dynamic accesses to the element represented with the metadata-entry will behave as if the metadata-entry does not exist.
This means that those dynamic accesses will throw a missing-registration error.
A type is reached at run time, right before the class-initialization routine starts for that type (class or interface), or any of the type's subtypes are reached.
For "typeReached": "ConditionType" that guards a metadata entry in the following example, the type is considered reached:
class SuperType {
static {
// ConditionType reached (subtype reached) => metadata entry available
}
}
class ConditionType extends SuperType {
static {
// ConditionType reached (before static initializer) => metadata entry available
}
static ConditionType singleton() {
// ConditionType reached (already initialized) => metadata entry available
}
}
public class App {
public static void main(String[] args) {
// ConditionType not reached => metadata entry not available
ConditionType.class;
// ConditionType not reached (ConditionType.class doesn't start class initialization) => metadata entry not available
ConditionType.singleton();
// ConditionType reached (already initialized) => metadata entry available
}
}
A type is also reached, if it is marked as initialize-at-build-time or any of its subtypes are marked as initialize-at-build-time and they exist on the classpath.
Array types are never marked as reached and as such cannot be used in conditions.
A conditional metadata entry is included into the image when the fully-qualified type is reachable at build time. This entry affects the image size, and it will be available at run time only when the condition is reached at run time.
You can find more examples of the metadata files in the GraalVM Reachability Metadata repository.
The reachability-metadata.json file contains three main sections: reflection, jni, and resources. Each section contains arrays of metadata entries with specific field configurations.
These fields are available in all sections (reflection, jni, and resources):
| Field | Type | Description | Example |
|---|---|---|---|
condition | Object | Conditional registration based on type reachability | "condition": {"typeReached": "com.example.Type"} |
condition.typeReached | String | Fully qualified class name that must be reached | "com.example.ConditionClass" |
Use these fields in the "reflection": [...] section to configure Java reflection access for your application:
| Field | Type | Description | Example |
|---|---|---|---|
type | String/Object | Class name, proxy definition, or lambda definition | "com.example.MyClass" |
type.proxy | Array | Ordered list of interfaces for proxy classes | {"proxy": ["java.lang.Runnable", "java.io.Serializable"]} |
type.lambda | Object | Lambda class definition | {"lambda": {...}} |
type.lambda.declaringClass | String | Class where lambda is declared | "com.example.LambdaHost" |
type.lambda.declaringMethod | Object | Method where lambda is declared (optional) | {"name": "methodName", "parameterTypes": [...]} |
type.lambda.interfaces | Array | Interfaces implemented by lambda | ["java.util.function.Function"] |
allDeclaredConstructors | Boolean | Enable access to all declared constructors | true |
allPublicConstructors | Boolean | Enable access to all public constructors | true |
allDeclaredMethods | Boolean | Enable access to all declared methods | true |
allPublicMethods | Boolean | Enable access to all public methods (including inherited) | true |
allDeclaredFields | Boolean | Enable access to all declared fields | true |
allPublicFields | Boolean | Enable access to all public fields (including inherited) | true |
methods | Array | List of specific methods to enable access to | [{"name": "methodName", "parameterTypes": [...]}] |
methods[].name | String | Method name | "toString" |
methods[].parameterTypes | Array | Fully qualified parameter type names | ["java.lang.String", "int"] |
fields | Array | List of specific fields to enable access to | [{"name": "fieldName"}] |
fields[].name | String | Field name | "value" |
unsafeAllocated | Boolean | Enable unsafe allocation via Unsafe.allocateInstance() | true |
serializable | Boolean | Register type for Java serialization | true |
Use these fields in the "jni": [...] section to configure Java Native Interface access from native code:
| Field | Type | Description | Example |
|---|---|---|---|
type | String | Fully qualified class name | "com.example.NativeClass" |
jniAccessible | Boolean | Make type accessible from JNI code | true |
allDeclaredConstructors | Boolean | Enable access to all declared constructors | true |
allPublicConstructors | Boolean | Enable access to all public constructors | true |
allDeclaredMethods | Boolean | Enable access to all declared methods | true |
allPublicMethods | Boolean | Enable access to all public methods (including inherited) | true |
allDeclaredFields | Boolean | Enable access to all declared fields | true |
allPublicFields | Boolean | Enable access to all public fields (including inherited) | true |
methods | Array | List of specific methods to enable access to | [{"name": "methodName", "parameterTypes": [...]}] |
methods[].name | String | Method name | "processData" |
methods[].parameterTypes | Array | Fully qualified parameter type names | ["java.lang.String", "int"] |
fields | Array | List of specific fields to enable access to | [{"name": "fieldName"}] |
fields[].name | String | Field name | "nativeHandle" |
unsafeAllocated | Boolean | Enable unsafe allocation via AllocObject | true |
Use these fields in the "resources": [...] section to configure classpath resources and resource bundles:
| Field | Type | Description | Example |
|---|---|---|---|
glob | String | Glob pattern for matching resource files | "META-INF/**/*.properties" |
bundle | String | Resource bundle base name | "com.example.messages.Bundle" |
module | String | Java module name (optional) | "com.example.app" |
For comprehensive examples of reachability metadata configuration, see the Sample Reachability Metadata section.
Native Image accepts the following types of reachability metadata:
java.lang.reflect.* API) enables Java code to examine its own classes, methods, fields, and their properties at run time.java.util.ResourceBundle) that enables Java code to load L10N resources.For all methods in this section Native Image will compute reachability at build time given that all the call arguments are constant. Providing constant arguments in code is a preferred way to provide metadata as it requires no duplication of information in external JSON files.
Reflection in Java starts with java.lang.Class that allows fetching further reflective elements such as methods and fields.
The class can be fetched reflectively via the following static functions on java.lang.Class:
java.lang.Class forName(java.lang.String) throws java.lang.ClassNotFoundExceptionjava.lang.Class forName(java.lang.String, boolean, java.lang.ClassLoader) throws java.lang.ClassNotFoundExceptionjava.lang.Class forName(java.lang.Module, java.lang.String)java.lang.Class arrayType() - requires metadata for the array type.
The classes can be also fetched reflectively loading a class from a name with java.lang.ClassLoader#loadClass(String).To provide metadata for the calls that fetch a Class reflectively, the following entry must be added to the reflection array in reachability-metadata.json:
{
"type": "FullyQualifiedReflectivelyAccessedType"
}
For proxy classes, the java.lang.Class is fetched with the following methods on java.lang.reflect.Proxy:
java.lang.Class getProxyClass(java.lang.ClassLoader, java.lang.Class[]) throws java.lang.IllegalArgumentExceptionjava.lang.Object newProxyInstance(java.lang.ClassLoader, java.lang.Class[], java.lang.reflect.InvocationHandler)Metadata, for proxy classes, is in the form an ordered collection of interfaces that defines a proxy:
{
"type": {
"proxy": ["FullyQualifiedInterface1", "...", "FullyQualifiedInterfaceN"]
}
}
To provide metadata for a lambda class, the following metadata must be added to the reflection array in
reachability-metadata.json
{
"type": {
"lambda": {
"declaringClass": "FullyQualifiedLambdaDeclaringType",
"declaringMethod": {
"name": "declaringMethodName",
"parameterType": [
"FullyQualifiedParameterType1",
"...",
"FullyQualifiedParameterType2"
]
},
"interfaces": [
"FullyQualifiedLambdaInterface1",
"...",
"FullyQualifiedLamdbaInterface2"
]
}
}
}
The "declaringClass" field specifies in which class, and the optional "declaringMethod" field specifies in which
method the lambda is defined.
If "declaringMethod" is not specified, the lambda class is searched through all methods of the specified declaring
class.
The "interfaces" field specifies which interfaces are implemented by the lambda class.
Such a definition can match multiple lambda classes. If that is the case, the registration entry applies to all those
classes.
Invocation of methods above without the provided metadata will result in throwing MissingReflectionRegistrationError which extends java.lang.Error and
should not be handled. Note that even if a type does not exist on the classpath, the methods above will throw a MissingReflectionRegistrationError.
The following methods on java.lang.Class will throw a MissingRegistrationError if the metadata is not provided for a given type:
Constructor getConstructor(Class[]) throws NoSuchMethodException,SecurityExceptionConstructor getDeclaredConstructor(Class[]) throws NoSuchMethodException,SecurityExceptionConstructor[] getConstructors() throws SecurityExceptionConstructor[] getDeclaredConstructors() throws SecurityExceptionMethod getMethod(String,Class[]) throws NoSuchMethodException,SecurityExceptionMethod getDeclaredMethod(String,Class[]) throws NoSuchMethodException,SecurityExceptionMethod[] getMethods() throws SecurityExceptionMethod[] getDeclaredMethods() throws SecurityExceptionField getField(String) throws NoSuchFieldException,SecurityExceptionField getDeclaredField(String) throws NoSuchFieldException,SecurityExceptionField[] getFields() throws SecurityExceptionField[] getDeclaredFields() throws SecurityExceptionRecordComponent[] getRecordComponents()Class[] getPermittedSubclasses()Object[] getSigners()Class[] getNestMembers()Class[] getClasses()Class[] getDeclaredClasses() throws SecurityExceptionFurthermore, all reflective lookups via java.lang.invoke.MethodHandles.Lookup will also require metadata for the type to be present, or they will throw MissingReflectionRegistrationError.
Note that for lambda-proxy classes, metadata can not be provided. This is a known issue that will be addressed in the future releases of GraalVM.
To reflectively invoke methods, specify the method signature in the metadata. See the Reflection Metadata reference table for complete field documentation.
{
"type": "TypeWhoseMethodsAreInvoked",
"methods": [
{"name": "<methodName1>", "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]},
{"name": "<methodName2>", "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]}
]
}
For convenience, you can enable access to groups of methods using the allDeclared* and allPublic* fields:
{
"type": "TypeWhoseMethodsAreInvoked",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true
}
In case the method-invocation metadata is missing, the following methods will throw a MissingReflectionRegistrationError:
java.lang.reflect.Method#invoke(Object, Object...)java.lang.reflect.Constructor#newInstance(Object...)java.lang.invoke.MethodHandle#invokeExact(Object...)java.lang.invoke.MethodHandle#invokeWithArguments (all overloaded versions)To reflectively access (get or set) field values, specify field names in the metadata. See the Reflection Metadata reference table for field configuration options.
{
"type": "TypeWhoseFieldValuesAreAccessed",
"fields": [{"name": "<fieldName1>"}, {"name": "<fieldNameI>"}, {"name": "<fieldNameN>"}]
}
For convenience, use allDeclaredFields and allPublicFields to enable access to all fields:
{
"type": "TypeWhoseFieldValuesAreAccessed",
"allDeclaredFields": true,
"allPublicFields": true
}
In case the field-value-access metadata is missing, the following methods will throw a MissingReflectionRegistrationError:
java.lang.reflect.Field#get(Object)java.lang.reflect.Field#set(Object, Object)java.lang.reflect.VarHandle.For unsafe allocation of a type via sun.misc.Unsafe#allocateInstance(Class<?>), or from native code via AllocObject(jClass), we must provide the following metadata:
{
"type": "FullyQualifiedUnsafeAllocatedType",
"unsafeAllocated": true
}
Otherwise, these methods will throw a MissingReflectionRegistrationError.
The overall definition of a type in JSON can have the following values:
{
"condition": {
"typeReached": "<condition-class>"
},
"type": "<class>|<proxy-interface-list>",
"fields": [
{"name": "<fieldName>"}
],
"methods": [
{"name": "<methodName>", "parameterTypes": ["<param-type>"]}
],
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredFields": true,
"allPublicFields": true,
"unsafeAllocated": true
}
Java Native Interface (JNI) allows native code to access arbitrary Java types and type members. Native Image cannot predict what such native code will lookup, write to or invoke. To build a native binary for a Java application that uses JNI to access Java values, JNI metadata is required.
For example, the following C code:
jclass clazz = FindClass(env, "jni/accessed/Type");
looks up the jni.accessed.Type class, which can then be used to instantiate jni.accessed.Type, invoke its methods or access its fields.
The metadata entry for the above call can only be provided via reachability-metadata.json. Specify
the jniAccessible field in the type entry in the reflection section:
{
"reflection": [
{
"type": "jni.accessed.Type",
"jniAccessibleType": true
}
]
}
Adding the metadata for a type does not allow to fetch all of its fields and methods with GetFieldID, GetStaticFieldID, GetStaticMethodID, and GetMethodID.
To access field values, provide field names. See the JNI Metadata reference table for complete configuration options:
{
"type": "jni.accessed.Type",
"jniAccessible": true,
"fields": [{"name": "value"}]
}
For comprehensive access, use the convenience fields:
{
"type": "jni.accessed.Type",
"jniAccessible": true,
"allDeclaredFields": true,
"allPublicFields": true
}
To call Java methods from JNI, specify method signatures:
{
"type": "jni.accessed.Type",
"jniAccessible": true,
"methods": [
{"name": "<methodName1>", "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]},
{"name": "<methodName2>", "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]}
]
}
For groups of methods, use the allDeclared* and allPublic* convenience fields:
{
"type": "jni.accessed.Type",
"jniAccessible": true,
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true
}
To allocate objects of a type with AllocObject, the unsafeAllocated field must be set, but the jniAccessible field
is not required:
{
"reflection": [
{
"type": "jni.accessed.Type",
"unsafeAllocated": true
}
]
}
Failing to provide metadata for an element that is dynamically accessed from native code will result in an exception (MissingJNIRegistrationError).
Note that most libraries that use JNI do not handle exceptions properly, so to see which elements are missing
--exact-reachability-metadatain combination with-XX:MissingRegistrationReportingMode=Warnmust be used.
The Foreign Function and Memory (FFM) API is an interface that enables Java code to interact with native code and vice versa.
In particular, it allows you to create downcall handles and upcall stubs.
To perform downcalls or upcalls at run time, supporting code must be generated at image build time.
Therefore, the native-image builder must be provided with descriptors that characterize the functions with which downcalls or upcalls can be performed at run time.
If the necessary metadata is not provided, a MissingForeignRegistrationError will be thrown at run time.
Java is capable of accessing any resource on the application class path, or the module path for which the requesting code has permission to access.
Resource metadata instructs the native-image builder to include specified resources and resource bundles in the produced binary.
A consequence of this approach is that some parts of the application that use resources for configuration (such as logging) are effectively configured at build time.
Native Image will detect calls to java.lang.Class#getResource and java.lang.Class#getResourceAsStream in which:
name) is a constant and automatically register such resources.The code below will work out of the box, because:
Example.class) as the receivername parameterclass Example {
public void conquerTheWorld() {
InputStream plan = Example.class.getResourceAsStream("plans/v2/conquer_the_world.txt");
}
}
Resource metadata is specified in the resources field. See the Resource Metadata reference table for complete configuration options.
{
"resources": [
{
"glob": "path1/level*/**"
}
]
}
The glob field uses a subset of glob-pattern rules:
*) and globstar (**) wildcards\*Given the following project structure:
app-root
└── src
└── main
└── resources
├── Resource0.txt
└── Resource1.txt
You can:
**/Resource*.txt ({ "glob":})**/Resource0.txt**/Resource0.txt and **/Resource1.txtFor every resource or resource bundle, it is possible to specify the module from which the resource or resource bundle should be taken.
You can specify a module name in the separate module field in each entry.
For example:
{
"resources": [
{
"module:": "library.module",
"glob": "resource-file.txt"
}
]
}
This will cause the native-image tool to only include resource-file.txt from the Java module library.module.
If other modules or the classpath containing resources that match the pattern resource-file.txt, only the one in library-module is registered to be included into a native executable.
Native Image will also ensure that the modules are guaranteed to be accessible at runtime.
Take the following code pattern:
InputStream resource = ModuleLayer.boot().findModule("library.module").getResourceAsStream(resourcePath);
It will always work as expected for resources registered as described above (even if the module does not contain any code that is considered reachable by static analysis).
There are two ways to see which resources were included in a native executable:
--emit build-report to generate a build report for your native executable.
There you can find information about all included resources under the Resources tab.-H:+GenerateEmbeddedResourcesFile to generate a JSON file embedded-resources.json, listing all included resources.For each registered resource you get:
unnamed if a resource does not belong to any module)Note: The size of a resource directory represents only the size of the names of all directory entries (not a sum of the content sizes).
Java localization support (java.util.ResourceBundle) enables loading L10N resources for specific locales. See the Resource Metadata reference table for bundle configuration options.
Specify bundles in the resources section:
{
"resources": [
{
"bundle": "your.pkg.Bundle"
}
]
}
To request a bundle from a specific module:
{
"resources": [
{
"module": "app.module",
"bundle": "your.pkg.Bundle"
}
]
}
Resource bundles are included for all locales that are included into the image.
It is also possible to specify which locales should be included in a native executable and which should be the default. For example, to switch the default locale to Swiss German and also include French and English, use the following options:
native-image -Duser.country=CH -Duser.language=de -H:IncludeLocales=fr,en
The locales are specified using language tags.
You can include all locales via -H:+IncludeAllLocales, but note that it increases the size of the resulting executable.
Java can serialize (or deserialize) any class that implements the Serializable interface.
Native Image supports serialization (or deserialization) with proper serialization metadata registration.
This is necessary because serialization usually requires reflective accesses to the object that is being serialized.
Native Image detects calls to ObjectInputFilter.Config#createFilter(String pattern) and if the pattern argument is constant, the exact classes mentioned in the pattern will be registered for serialization.
For example, the following pattern will register the class pkg.SerializableClass for serialization:
var filter = ObjectInputFilter.Config.createFilter("pkg.SerializableClass;!*;")
objectInputStream.setObjectInputFilter(proof);
Using this pattern has a positive side effect of improving security on the JVM as only pkg.SerializableClass can be received by the objectInputStream.
Wildcard patterns do the serialization registration only for lambda-proxy classes of an enclosing class. For example, to register lambda serialization in an enclosing class pkg.LambdaHolder use:
ObjectInputFilter.Config.createFilter("pkg.LambdaHolder$$Lambda*;")
Patterns like "pkg.**" and "pkg.Prefix*" will not perform serialization registration as they are too general and would increase image size significantly.
For calls to the sun.reflect.ReflectionFactory#newConstructorForSerialization(java.lang.Class) and sun.reflect.ReflectionFactory#newConstructorForSerialization(java.lang.Class, ) native image detects calls to these functions when all arguments and the receiver are constant. For example, the following call will register SerializlableClass for serialization:
ReflectionFactory.getReflectionFactory().newConstructorForSerialization(SerializableClass.class);
To create a custom constructor for serialization use:
var constructor = SuperSuperClass.class.getDeclaredConstructor();
var newConstructor = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(BaseClass.class, constructor);
Proxy classes can only be registered for serialization via the JSON files.
Serialization metadata is specified in the reflection section. See the Reflection Metadata reference table for the serializable field documentation.
For regular classes:
{
"reflection": [
{
"type": "serialized.Type",
"serializable": true
}
]
}
For proxy classes:
{
"reflection": [
{
"type": {
"proxy": ["FullyQualifiedInterface1", "...", "FullyQualifiedInterfaceN"],
"serializable": true
}
}
]
}
In rare cases an application might explicitly make calls to:
ReflectionFactory.newConstructorForSerialization(Class<?> cl, Constructor<?> constructorToCall);
The specified constructorToCall differs from the one that would be automatically used during regular serialization of cl.
When a class is registered for run-time serialization, all potential custom constructors are automatically registered.
As a result, this use case does not require any additional metadata.
See below is a sample reachability metadata configuration that you can use in reachabilty-metadata.json:
{
"reflection": [
{
"type": "reflectively.accessed.Type",
"fields": [
{
"name": "field1"
}
],
"methods": [
{
"name": "method1",
"parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]
}
],
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"unsafeAllocated": true,
"serializable": true
}
],
"jni": [
{
"type": "jni.accessed.Type",
"fields": [
{
"name": "field1"
}
],
"methods": [
{
"name": "method1",
"parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]
}
],
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredFields": true,
"allPublicFields": true,
"allDeclaredMethods": true,
"allPublicMethods": true
}
],
"resources": [
{
"module": "optional.module.of.a.resource",
"glob": "path1/level*/**"
},
{
"bundle": "fully.qualified.bundle.name"
}
],
"foreign": {
"downcalls": [
{
"returnType": "<return-type>",
"parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]
}
],
"upcalls": [
{
"returnType": "<return-type>",
"parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]
}
],
"directUpcalls": [
{
"class": "org.example.SomeClass",
"method": "method1",
"returnType": "<return-type>",
"parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]
}
]
}
}
Java has support for loading new classes from bytecode at run time, which is not possible in Native Image as all classes must be known at build time (the "closed-world assumption"). To overcome this issue there are the following options:
--initialize-at-build-time=<class_name> as the build argument.java -agentlib:native-image-agent=config-output-dir=<config-dir>,experimental-class-define-support <application-arguments>.
At runtime, if there is an attempt to load a class with the same name and bytecode as one of the classes encountered during tracing, the predefined class will be supplied to the application.Predefined classes metadata is specified in a predefined-classes-config.json file and conform to the JSON schema defined in predefined-classes-config-schema-v1.0.0.json. The schema also includes further details and explanations how this configuration works. Here is the example of the predefined-classes-config.json:
[
{
"type": "agent-extracted",
"classes": [
{
"hash": "<class-bytecodes-hash>",
"nameInfo": "<class-name"
}
]
}
]
Note: Predefined classes metadata is not meant to be manually written. Note: Predefined classes are the best-effort approach for legacy projects, and they are not guaranteed to work.