Search before asking
Version
| Component |
Version |
| OS |
Windows 11 Pro 10.0.26200 64-bit |
| JDK |
OpenJDK 25.0.2 (build 25.0.2+10-69, 64-bit Server VM) |
| fory-core (broken) |
1.2.0 |
| fory-core (working) |
1.1.0 |
Component(s)
Java
Minimal reproduce step
import org.apache.fory.Fory;
import org.apache.fory.ThreadSafeFory;
import org.apache.fory.config.CompatibleMode;
import org.apache.fory.config.Language;
// Minimal model
static class Item {
String value;
int count;
Item() {}
Item(String value, int count) { this.value = value; this.count = count; }
}
static class Container {
List<Item> items;
Container() {}
Container(List<Item> items) { this.items = items; }
}
public static void main(String[] args) throws Exception {
ThreadSafeFory fory = Fory.builder()
.withLanguage(Language.JAVA)
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.withAsyncCompilation(true)
.requireClassRegistration(true)
.buildThreadSafeForyPool(4);
fory.register(Container.class);
fory.register(Item.class);
Container obj = new Container(List.of(new Item("x", 1), new Item("y", 2)));
byte[] bytes = fory.serialize(obj);
fory.deserialize(bytes);
Thread.sleep(3000); // wait for async JIT to fire
}
What did you expect to see?
No errors. Async JIT compiles the serializer for Container and injects it silently.
What did you see instead?
java.lang.UnsupportedOperationException: can't get field offset on a hidden class:
org.apache.fory.serializer.Serializer
ContainerForyCodec_0/0x000000009d145c00.serializer
at jdk.unsupported/sun.misc.Unsafe.objectFieldOffset(Unsafe.java:900)
at org.apache.fory.reflect.InstanceFieldAccessors$InstanceAccessor.fieldOffset(InstanceFieldAccessors.java:136)
at org.apache.fory.reflect.ReflectionUtils.setObjectFieldValue(ReflectionUtils.java:471)
at org.apache.fory.builder.Generated$GeneratedSerializer$1.onNotifyResult(Generated.java:85)
at org.apache.fory.builder.JITContext.lambda$registerSerializerJITCallback$0(JITContext.java:96)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(...)
java.lang.NullPointerException: Cannot invoke "java.util.List.iterator()"
because the return value of "java.util.Map.get(Object)" is null
at org.apache.fory.builder.JITContext.lambda$registerSerializerJITCallback$0(JITContext.java:95)
Triggered on first serialization of any class containing List where T is a user-defined type. Does not trigger for flat classes or List. Does not occur on Fory 1.1.0.
Anything Else?
Root cause analysis:
PR #3702 (feat(java): remove sun.misc.Unsafe for jdk25) changed how Fory defines JIT-generated serializer classes. On JDK 25 a VarHandle-based MR-JAR path is used correctly. On JDK 15–24, the generated serializer class is now defined as a hidden class (via Lookup.defineHiddenClass()), but the callback in onNotifyResult still injects the serializer field via Unsafe.objectFieldOffset() — which the JVM has explicitly forbidden on hidden classes since JEP 371 (JDK 15). This code path was not covered by the MR-JAR fix.
Workaround: withAsyncCompilation(false)
Are you willing to submit a PR?
Search before asking
Version
Component(s)
Java
Minimal reproduce step
What did you expect to see?
No errors. Async JIT compiles the serializer for Container and injects it silently.
What did you see instead?
Triggered on first serialization of any class containing List where T is a user-defined type. Does not trigger for flat classes or List. Does not occur on Fory 1.1.0.
Anything Else?
Root cause analysis:
PR #3702 (feat(java): remove sun.misc.Unsafe for jdk25) changed how Fory defines JIT-generated serializer classes. On JDK 25 a VarHandle-based MR-JAR path is used correctly. On JDK 15–24, the generated serializer class is now defined as a hidden class (via Lookup.defineHiddenClass()), but the callback in onNotifyResult still injects the serializer field via Unsafe.objectFieldOffset() — which the JVM has explicitly forbidden on hidden classes since JEP 371 (JDK 15). This code path was not covered by the MR-JAR fix.
Workaround: withAsyncCompilation(false)
Are you willing to submit a PR?