Razrešavanje reflektivnih poziva u GraalVM-u
Motivacija
Kompajler GraalVM Native Image prevodi Java programe u izvršne datoteke pre njihovog pokretanja. Prilikom kompilacije, koristi se pretpostavka zatvorenog sveta: sav kod koji program može da izvrši mora biti dostupan tokom prevođenja. Refleksija otežava analizu dostižnosti koda, jer omogućava pristup klasama, metodama i poljima na osnovu vrednosti koje mogu biti poznate tek tokom izvršavanja. Deo takvih poziva moguće je automatski razrešiti statičkom analizom, čime se smanjuje potreba za ručnim zadavanjem konfiguracije za refleksiju.
Pristup zasnovan na analizi Graal grafovske međureprezentacije zavisi od optimizacija kompajlera. Zbog toga promene u optimizacijama mogu da promene skup automatski razrešenih poziva i utiču na ponašanje generisanog programa. Cilj istraživanja je da pravila razrešavanja budu predvidljiva i nezavisna od kompajlerskih optimizacija.
Analiza Java bajtkoda
U okviru istraživanja razvijeni su proširivi okvir za analizu toka podataka u Java bajtkodu (Stefanović et al., 2025a) i analiza za automatsko razrešavanje reflektivnih poziva (Stefanović et al., 2024), integrisani u sistem GraalVM i detaljno opisani u master radu (Stefanović, 2025b). Analiza prati tok podataka unutar pojedinačnog metoda, prenoseći informacije o poznatim vrednostima kroz stek operanada i lokalne promenljive. Reflektivni poziv razrešava se kada analiza može da utvrdi potrebne vrednosti njegovih argumenata, nakon čega se taj poziv zamenjuje rezultatom njegovog izvršavanja.
Evaluacija
Rešenje je evaluirano na referentnoj veb aplikaciji Spring PetClinic. Analiza bajtkoda razrešila je 211 reflektivnih poziva, dok je analiza Graal grafova razrešila 222. To predstavlja oko 5% manje razrešenih poziva u ovom eksperimentu, uz nezavisnost od kompilatorskih optimizacija.
Broj razrešenih reflektivnih poziva tokom kompilacije aplikacije Spring PetClinic:
| Metod | Analiza Graal grafova | Analiza bajtkoda |
|---|---|---|
| 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 |
| Ukupno | 222 | 211 |
Zahvalnica
Razrešavanje reflektivnih poziva u GraalVM-u se razvija u okviru istraživačke saradnje između Matematičkog fakulteta i kompanije Oracle LLC.
Literatura
- 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

