Dynamic Access Inference in GraalVM
Motivation
The GraalVM Native Image compiler translates Java programs into executable files before they are run. Compilation relies on the closed-world assumption: all code that a program may execute must be available at compile time. Reflection complicates code reachability analysis because it allows access to classes, methods, and fields based on values that may only become known at runtime. Some of these calls can be resolved automatically through static analysis, reducing the need to provide reflection configuration manually.
The approach based on analysing Graal's graph intermediate representation depends on compiler optimisations. Changes to these optimisations can therefore alter the set of automatically resolved calls and affect the behaviour of the generated program. The goal of this research is to make resolution rules predictable and independent of compiler optimisations.
Java Bytecode Analysis
This research produced an extensible framework for data-flow analysis of Java bytecode (Stefanović et al., 2025a) and an analysis for automatically resolving reflective calls (Stefanović et al., 2024), both integrated into GraalVM and described in detail in the master's thesis (Stefanović, 2025b). The analysis tracks data flow within individual methods, propagating information about known values through the operand stack and local variables. A reflective call is resolved when the analysis can determine the required argument values, after which the call is replaced with the result of its execution.
Evaluation
The solution was evaluated using the Spring PetClinic reference web application. Bytecode analysis resolved 211 reflective calls, while Graal graph analysis resolved 222. This represents approximately 5% fewer resolved calls in this experiment, while ensuring independence from compiler optimisations.
Number of reflective calls resolved during compilation of Spring PetClinic:
| Method | Graal graph analysis | Bytecode analysis |
|---|---|---|
| java.lang.Class | ||
forName(String) |
77 | 76 |
forName(String, boolean, ClassLoader) |
18 | 12 |
getField(String) |
0 | 0 |
getDeclaredField(String) |
16 | 16 |
getConstructor(Class[]) |
5 | 5 |
getDeclaredConstructor(Class[]) |
5 | 5 |
getMethod(String, Class[]) |
40 | 37 |
getDeclaredMethod(String, Class[]) |
20 | 20 |
getFields() |
2 | 2 |
getDeclaredFields() |
6 | 6 |
getConstructors() |
0 | 0 |
getDeclaredConstructors() |
0 | 0 |
getMethods() |
1 | 1 |
getDeclaredMethods() |
6 | 6 |
getClasses() |
0 | 0 |
getDeclaredClasses() |
0 | 0 |
getNestMembers() |
0 | 0 |
getPermittedSubclasses() |
0 | 0 |
getRecordComponents() |
0 | 0 |
getSigners() |
0 | 0 |
| java.lang.invoke.MethodHandles.Lookup | ||
findClass(String) |
0 | 0 |
findVirtual(Class, String, MethodType) |
2 | 2 |
findStatic(Class, String, MethodType) |
6 | 6 |
findConstructor(Class, MethodType) |
0 | 0 |
findGetter(Class, String, Class) |
0 | 0 |
findStaticGetter(Class, String, Class) |
0 | 0 |
findSetter(Class, String, Class) |
0 | 0 |
findStaticSetter(Class, String, Class) |
0 | 0 |
findVarHandle(Class, String, Class) |
18 | 17 |
findStaticVarHandle(Class, String, Class) |
0 | 0 |
| Total | 222 | 211 |
Acknowledgements
Dynamic Access Inference in GraalVM is developed as part of the research collaboration between the Faculty of Mathematics and Oracle LLC.
References
- Aleksandar Stefanović, Ivan Ristović, Milena Vujošević Janičić: Constant Folding of Reflective Calls via Static Analysis of Java Bytecode. Proceedings of the 14th Symposium "Mathematics and Applications", 2024. URL
- Aleksandar Stefanović, Ivan Ristović, Milena Vujošević Janičić: Extensible Java Bytecode Data-Flow Analysis JVMCI Framework. Proceedings of the 15th Symposium "Mathematics and Applications", 2025a. URL
- Aleksandar Stefanović: Razrešavanje reflektivnih poziva statičkom analizom Java bajtkoda. Masters Thesis, Faculty of Mathematics, University of Belgrade, 2025b. URL

