diff --git a/java/fory-core/src/main/java/org/apache/fory/Fory.java b/java/fory-core/src/main/java/org/apache/fory/Fory.java index c1781d5ca4..c29c72d8e3 100644 --- a/java/fory-core/src/main/java/org/apache/fory/Fory.java +++ b/java/fory-core/src/main/java/org/apache/fory/Fory.java @@ -19,6 +19,7 @@ package org.apache.fory; +import com.google.common.base.Objects; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -125,11 +126,13 @@ public final class Fory implements BaseFory { private int copyDepth; private final boolean copyRefTracking; private final IdentityMap originToCopyMap; + private int configHash; public Fory(ForyBuilder builder, ClassLoader classLoader) { // Avoid set classLoader in `ForyBuilder`, which won't be clear when // `org.apache.fory.ThreadSafeFory.clearClassLoader` is called. config = new Config(builder); + this.configHash = config.hashCode(); crossLanguage = config.isXlang(); this.refTracking = config.trackingRef(); this.copyRefTracking = config.copyRef(); @@ -235,11 +238,13 @@ public void registerUnion( @Override public void registerSerializer(Class type, Class serializerClass) { getTypeResolver().registerSerializer(type, serializerClass); + this.configHash = Objects.hashCode(configHash, type, serializerClass); } @Override public void registerSerializer(Class type, Serializer serializer) { getTypeResolver().registerSerializer(type, serializer); + this.configHash = Objects.hashCode(configHash, type, serializer.getClass()); } @Override @@ -247,6 +252,10 @@ public void registerSerializer(Class type, Function> seri getTypeResolver().registerSerializer(type, serializerCreator.apply(this)); } + public int getConfigHash() { + return configHash; + } + @Override public void registerSerializerAndType( Class type, Class serializerClass) { diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java index 259dd88201..77b3426350 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java @@ -242,7 +242,7 @@ public String codecClassName(Class beanClass) { nameBuilder.append("Codec").append(codecSuffix()); Map subGenerator = idGenerator.computeIfAbsent(nameBuilder.toString(), k -> new ConcurrentHashMap<>()); - String key = fory.getConfig().getConfigHash() + "_" + CodeGenerator.getClassUniqueId(beanClass); + String key = fory.getConfigHash() + "_" + CodeGenerator.getClassUniqueId(beanClass); Integer id = subGenerator.get(key); if (id == null) { synchronized (subGenerator) { diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/CodecUtils.java b/java/fory-core/src/main/java/org/apache/fory/builder/CodecUtils.java index 5bce2db3be..d6e4b0d0f8 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/CodecUtils.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/CodecUtils.java @@ -153,7 +153,7 @@ private static CodeGenerator getCodeGenerator( private static Class> loadSerializer( String name, Class cls, Fory fory, Callable>> func) { - int configHash = fory.getConfig().getConfigHash(); + int configHash = fory.getConfigHash(); if (GraalvmSupport.IN_GRAALVM_NATIVE_IMAGE) { Tuple3, Integer> key = Tuple3.of(name, cls, configHash); Class serializerClass = graalvmSerializers.get(key); diff --git a/java/fory-core/src/main/java/org/apache/fory/config/Config.java b/java/fory-core/src/main/java/org/apache/fory/config/Config.java index e07ada4ec1..63ab82d360 100644 --- a/java/fory-core/src/main/java/org/apache/fory/config/Config.java +++ b/java/fory-core/src/main/java/org/apache/fory/config/Config.java @@ -21,9 +21,6 @@ import java.io.Serializable; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicInteger; import org.apache.fory.Fory; import org.apache.fory.meta.MetaCompressor; import org.apache.fory.serializer.Serializer; @@ -60,7 +57,6 @@ public class Config implements Serializable { private final boolean asyncCompilationEnabled; private final boolean deserializeUnknownClass; private final boolean scalaOptimizationEnabled; - private transient int configHash; private final UnknownEnumValueStrategy unknownEnumValueStrategy; private final boolean serializeEnumByName; private final int bufferSizeLimitBytes; @@ -364,18 +360,6 @@ public int hashCode() { scalaOptimizationEnabled); } - private static final AtomicInteger counter = new AtomicInteger(0); - // Different config instance with equality will be hold only one instance, no memory - // leak will happen. - private static final ConcurrentMap configIdMap = new ConcurrentHashMap<>(); - - public int getConfigHash() { - if (configHash == 0) { - configHash = configIdMap.computeIfAbsent(this, k -> counter.incrementAndGet()); - } - return configHash; - } - /** Returns max depth for deserialization, when depth exceeds, an exception will be thrown. */ public int maxDepth() { return maxDepth; diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java index ca6133a7bb..66a4816d40 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java @@ -240,7 +240,7 @@ public ClassResolver(Fory fory) { typeInfoCache = NIL_TYPE_INFO; extRegistry.classIdGenerator = NONEXISTENT_META_SHARED_ID + 1; shimDispatcher = new ShimDispatcher(fory); - _addGraalvmClassRegistry(fory.getConfig().getConfigHash(), this); + _addGraalvmClassRegistry(fory.getConfigHash(), this); } @Override @@ -498,7 +498,7 @@ public void register(Class cls, String namespace, String name) { compositeNameBytes2TypeInfo.put( new TypeNameBytes(nsBytes.hashCode, nameBytes.hashCode), typeInfo); extRegistry.registeredClasses.put(fullname, cls); - GraalvmSupport.registerClass(cls, fory.getConfig().getConfigHash()); + GraalvmSupport.registerClass(cls, fory.getConfigHash()); } @Override @@ -518,7 +518,7 @@ public void registerUnion(Class cls, long userId, Serializer serializer) { } updateTypeInfo(cls, typeInfo); extRegistry.registeredClasses.put(cls.getName(), cls); - GraalvmSupport.registerClass(cls, fory.getConfig().getConfigHash()); + GraalvmSupport.registerClass(cls, fory.getConfigHash()); } @Override @@ -550,7 +550,7 @@ public void registerUnion(Class cls, String namespace, String name, Serialize compositeNameBytes2TypeInfo.put( new TypeNameBytes(nsBytes.hashCode, nameBytes.hashCode), typeInfo); extRegistry.registeredClasses.put(fullname, cls); - GraalvmSupport.registerClass(cls, fory.getConfig().getConfigHash()); + GraalvmSupport.registerClass(cls, fory.getConfigHash()); } /** @@ -623,7 +623,7 @@ private void registerInternalImpl(Class cls, int typeId) { } updateTypeInfo(cls, typeInfo); extRegistry.registeredClasses.put(cls.getName(), cls); - GraalvmSupport.registerClass(cls, fory.getConfig().getConfigHash()); + GraalvmSupport.registerClass(cls, fory.getConfigHash()); } private void registerUserImpl(Class cls, int userId) { @@ -640,7 +640,7 @@ private void registerUserImpl(Class cls, int userId) { } updateTypeInfo(cls, typeInfo); extRegistry.registeredClasses.put(cls.getName(), cls); - GraalvmSupport.registerClass(cls, fory.getConfig().getConfigHash()); + GraalvmSupport.registerClass(cls, fory.getConfigHash()); } private int buildUserTypeId(Class cls, Serializer serializer) { @@ -2007,7 +2007,7 @@ public void ensureSerializersCompiled() { } classInfoMap.forEach( (cls, classInfo) -> { - GraalvmSupport.registerClass(cls, fory.getConfig().getConfigHash()); + GraalvmSupport.registerClass(cls, fory.getConfigHash()); if (classInfo.serializer == null) { if (isSerializable(classInfo.cls)) { createSerializer0(cls); diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java index 1aacf976da..7a55125d39 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java @@ -1439,7 +1439,7 @@ public static void _addGraalvmClassRegistry(int foryConfigHash, ClassResolver cl } final GraalvmSupport.GraalvmClassRegistry getGraalvmClassRegistry() { - return GraalvmSupport.getClassRegistry(fory.getConfig().getConfigHash()); + return GraalvmSupport.getClassRegistry(fory.getConfigHash()); } final Class getGraalvmSerializerClass(Serializer serializer) { diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java index 76d0366b48..ebbd2a347e 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java @@ -180,7 +180,7 @@ public void register(Class type, long userTypeId) { TypeInfo typeInfo = classInfoMap.get(type); if (type.isArray()) { buildTypeInfo(type); - GraalvmSupport.registerClass(type, fory.getConfig().getConfigHash()); + GraalvmSupport.registerClass(type, fory.getConfigHash()); return; } Serializer serializer = null; @@ -284,7 +284,7 @@ private void register( String qualifiedName = qualifiedName(namespace, typeName); qualifiedType2TypeInfo.put(qualifiedName, typeInfo); extRegistry.registeredClasses.put(qualifiedName, type); - GraalvmSupport.registerClass(type, fory.getConfig().getConfigHash()); + GraalvmSupport.registerClass(type, fory.getConfigHash()); if (serializer == null) { if (type.isEnum()) { typeInfo.serializer = new EnumSerializer(fory, (Class) type); @@ -1260,7 +1260,7 @@ private boolean isEnum(int internalTypeId) { public void ensureSerializersCompiled() { classInfoMap.forEach( (cls, classInfo) -> { - GraalvmSupport.registerClass(cls, fory.getConfig().getConfigHash()); + GraalvmSupport.registerClass(cls, fory.getConfigHash()); if (classInfo.serializer != null) { // Trigger serializer initialization and resolution for deferred serializers if (classInfo.serializer diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/TimeSerializersTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/TimeSerializersTest.java index 549c3134e8..922272b0a8 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/TimeSerializersTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/TimeSerializersTest.java @@ -337,8 +337,10 @@ public void testTimeStructRef() { { TimeStructRef struct = createTimeStructRef(new TimeStructRef()); TimeStructRef struct2 = (TimeStructRef) serDeCheck(fory, struct); - // TimeStructRef serializer already generated, enable ref tracking doesn't take effect. - Assert.assertNotSame(struct2.date1, struct2.date2); + + // Despite a TimeStructRef codegen serializer having already been generated, the change to + // enable ref tracking should now take effect due to improvements to the codegen caching + Assert.assertSame(struct2.date1, struct2.date2); } { TimeStructRef struct = createTimeStructRef(new TimeStructRef2()); diff --git a/java/fory-core/src/test/java/org/apache/fory/xlang/RegisterTest.java b/java/fory-core/src/test/java/org/apache/fory/xlang/RegisterTest.java index 6c3198d8ee..8daee15230 100644 --- a/java/fory-core/src/test/java/org/apache/fory/xlang/RegisterTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/xlang/RegisterTest.java @@ -128,4 +128,52 @@ public void testJava(boolean enableCodegen) { EmptyWrapper newWrapper = (EmptyWrapper) fory2.deserialize(buffer2); Assert.assertEquals(newWrapper, new EmptyWrapper()); } + + @Test + public void testCodegenCacheIsolation() { + Fory foryA = + Fory.builder() + .withLanguage(Language.XLANG) + .withCompatibleMode(CompatibleMode.COMPATIBLE) + .withCodegen(true) + .build(); + foryA.register(Color.class, 101); + foryA.register(MyStruct.class, 102); + foryA.register(MyExt.class, 103); + foryA.registerSerializer(MyExt.class, MyExtSerializer.class); + foryA.register(MyWrapper.class, 104); + + MyWrapper wrapperA = new MyWrapper(); + wrapperA.color = Color.Red; + wrapperA.my_struct = new MyStruct(10); + wrapperA.my_ext = new MyExt(20); + + Fory foryB = + Fory.builder() + .withLanguage(Language.XLANG) + .withCompatibleMode(CompatibleMode.COMPATIBLE) + .withCodegen(true) + .build(); + foryB.register(Color.class, 101); + foryB.register(MyStruct.class, 102); + foryB.register(MyExt.class, 103); + // NO MyExtSerializer registered + foryB.register(MyWrapper.class, 104); + + MyWrapper wrapperB = new MyWrapper(); + wrapperB.color = Color.Blue; + wrapperB.my_struct = new MyStruct(30); + wrapperB.my_ext = new MyExt(40); + + try { + byte[] serializedByB = foryB.serialize(wrapperB); + MyWrapper deserializedB = (MyWrapper) foryB.deserialize(serializedByB); + + Assert.assertNotNull(deserializedB); + Assert.assertEquals(deserializedB.my_ext.id, 40); + + } catch (Exception e) { + Assert.fail("foryB tried to use foryA's codegen. Exception: " + e.getMessage()); + } + } }