-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
Goal
Reduce image size and configuration size by limiting useless reflection capabilities being generated for methods never invoked through reflection and enabling reflection queries without configuration, while maintaining the option to fine-tune the reflection metadata being included for image size-sensitive applications.
Context
Reflection operation types
The Native Image implementation of Java’s reflection API currently doesn’t distinguish between two types of reflective operations:
- Queries: only require metadata about class members (i.e. Class.getMethod(s))
- Accesses: require the member to actually be present in the image (i.e. Method.invoke)
Shortcomings of the current implementation
This design decision leads to access capabilities being provided for members that are only queried but never accessed. This has an impact on the final image size mainly due to two factors:
- Those methods are wrongly marked as reachable, which can lead to more unreachable code being included in the image;
- A proxy class providing invocation capabilities is generated for all reflection-accessible methods but is never executed.
This only happens for methods and constructors, so the following proposal focuses solely on those.
Example
Given the following code (lightly edited for space):
class Test {
public static void main() {
callMethodsWithAnnotation(Test.class, Tag.class);
}
static void callMethodsWithAnnotation(Class<?> clazz, Class<?> annotationClazz) {
for (Method m : clazz.getDeclaredMethods())
if (m.getAnnotation(annotationClazz) != null)
m.invoke(null);
}
@Tag
void taggedMethod() { ... }
void deadCode() { ... }
}
The current reflection configuration for this program is as follows:
[{
"name":"Test",
"allDeclaredMethods":true
}]
As a consequence, useless reflection invocation capabilities are created for the main, callMethodsWithAnnotation and, more importantly, deadCode, which could force the inclusion of an arbitrary amount of unreachable code into the image.
Evaluation of impact on image size and configuration
Protocol
We measured the impact of the following changes on various regular and microservice benchmarks:
- Image size reduction when removing invocation capabilities for queried-only methods;
- Image size increase when including metadata required for reflection queries in all reachable classes;
- Configuration size reduction when including only invoked methods.
Results
The results show that the reduction in image size gained through the elimination of reachability analysis false positives and the non-inclusion of useless proxy classes is larger than the size increase caused by full metadata inclusion in all evaluated cases, this difference being quite important on some microservice benchmarks, particularly those based on the Spring and Micronaut frameworks (up to 20%).
The impact on configuration size is also positive, with the added bonus that the configuration is simplified by not needing the allDeclaredMethods and allPublicMethods fields. In most cases at most one of the methods included by these fields is actually invoked at runtime.
Proposal
Based on the results and to address the shortcomings presented above, we propose the following:
- Only require actually invoked methods to be present in the reflection configuration;
- Include metadata required for reflection queries by default in all classes;
- Add a mode to enable fine tuning of the metadata being included in the image. This mode is enabled by the
--configure-reflection-metadataNative Image option, and makes use of the following fields specifying methods being queried but not invoked:queriedMethods, holding a list of methods;- the following boolean fields:
queryAllDeclaredMethods,queryAllPublicMethods,queryAllDeclaredConstructors,queryAllPublicConstructors.
The Native Image agent will be updated to output valid configuration files with or without (by default) the fine-tuning fields. Fine-tuning can be enabled by specifying the track-reflection-metadata option. Example:
java -agentlib:native-image-agent=track-reflection-metadata ...
Example
Proposed new configuration for the example presented above:
[{
"name":"Test",
"methods":[{"name":"taggedMethod", "parameterTypes":[]}]
}]
Proposed configuration for the same code in optimized mode:
[{
"name":"Test",
"queryAllDeclaredMethods":true,
"methods":[{"name":"taggedMethod", "parameterTypes":[]}]
}]

