What kind of code is removed in GraalVM
In GraalVM Native Image mode, the compiler removes unused code to reduce binary size. This means if a class, method, or field is:
❌ Not directly used anywhere in the code AND
❌ Not explicitly registered for reflection
Then GraalVM completely removes it during compilation.
1️⃣ Example: Code That Gets Removed
Consider this Java class:
class UnusedClass {
public void sayHello() {
System.out.println("Hello from UnusedClass!");
}
}
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
What Happens in GraalVM Native Image Mode?
UnusedClass
is never instantiated or referenced in the code.- Since no reflection configuration is provided, GraalVM removes the class completely.
- The resulting native binary doesn’t contain
UnusedClass
at all.
🚨 If later, some code tries to use reflection to access UnusedClass
, it will fail with ClassNotFoundException
.
2️⃣ Example: Reflection Without Registration (Fails in Native Image)
public class Main {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("Test");
Object obj = clazz.getDeclaredConstructor().newInstance();
clazz.getMethod("hello").invoke(obj);
}
}
class Test {
public void hello() {
System.out.println("Hello from Test!");
}
}
💡 Why does this fail in Native Image?
- The
Test
class is never instantiated or directly referenced. - GraalVM sees no explicit usage and removes it during compilation.
- At runtime,
Class.forName("Test")
fails because Test no longer exists.
✔️ Solution: Register it for reflection using @ReflectiveAccess or a JSON config.
3️⃣ Example: Explicit Registration (Prevents Removal)
Solution 1: Use GraalVM Annotation
import com.oracle.svm.core.annotate.ReflectiveAccess;
@ReflectiveAccess // Tells GraalVM to keep this class
class Test {
public void hello() {
System.out.println("Hello from Test!");
}
}
Solution 2: Use Reflection Config File
Manually tell GraalVM to keep Test
by creating a reflect-config.json file:
[
{
"name": "Test",
"allDeclaredMethods": true,
"allDeclaredConstructors": true
}
]
Then compile the native image with:
native-image --reflect-config=reflect-config.json -jar myapp.jar
💡 Now, GraalVM keeps Test
and its methods, allowing reflection to work!