This is an automated email from the ASF dual-hosted git repository.

wangweipeng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-fury.git


The following commit(s) were added to refs/heads/main by this push:
     new 0437556e feat(java): avoid recompilation when gc happens for memory 
pressure (#1411)
0437556e is described below

commit 0437556ef4f3445869923f6b90806f565fda0e33
Author: Shawn Yang <[email protected]>
AuthorDate: Sun Mar 17 18:47:34 2024 +0800

    feat(java): avoid recompilation when gc happens for memory pressure (#1411)
---
 .../java/org/apache/fury/builder/CodecUtils.java   | 22 ++++++++--
 .../org/apache/fury/codegen/CodeGenerator.java     | 16 ++++----
 .../org/apache/fury/resolver/ClassResolver.java    | 16 ++++++++
 .../main/java/org/apache/fury/util/DelayedRef.java | 48 ++++++++++++++++++++++
 .../org/apache/fury/codegen/CodeGeneratorTest.java |  6 +--
 5 files changed, 94 insertions(+), 14 deletions(-)

diff --git 
a/java/fury-core/src/main/java/org/apache/fury/builder/CodecUtils.java 
b/java/fury-core/src/main/java/org/apache/fury/builder/CodecUtils.java
index e5cd5a54..d13be51e 100644
--- a/java/fury-core/src/main/java/org/apache/fury/builder/CodecUtils.java
+++ b/java/fury-core/src/main/java/org/apache/fury/builder/CodecUtils.java
@@ -24,6 +24,7 @@ import java.util.Collections;
 import org.apache.fury.Fury;
 import org.apache.fury.codegen.CodeGenerator;
 import org.apache.fury.codegen.CompileUnit;
+import org.apache.fury.resolver.ClassResolver;
 import org.apache.fury.resolver.FieldResolver;
 import org.apache.fury.serializer.Serializer;
 import org.apache.fury.type.ClassDef;
@@ -81,14 +82,29 @@ public class CodecUtils {
     if (beanClassClassLoader == null) {
       beanClassClassLoader = fury.getClass().getClassLoader();
     }
+    ClassResolver classResolver = fury.getClassResolver();
     try {
       // generated code imported fury classes.
       beanClassClassLoader.loadClass(Fury.class.getName());
-      codeGenerator = 
CodeGenerator.getSharedCodeGenerator(beanClassClassLoader);
+      codeGenerator = classResolver.getCodeGenerator(beanClassClassLoader);
+      if (codeGenerator == null) {
+        codeGenerator = 
CodeGenerator.getSharedCodeGenerator(beanClassClassLoader);
+        // Hold strong reference of {@link CodeGenerator}, so the referent of 
`DelayedRef`
+        // won't be null.
+        classResolver.setCodeGenerator(beanClassClassLoader, codeGenerator);
+      }
     } catch (ClassNotFoundException e) {
       codeGenerator =
-          CodeGenerator.getSharedCodeGenerator(
-              beanClassClassLoader, fury.getClass().getClassLoader());
+          classResolver.getCodeGenerator(beanClassClassLoader, 
fury.getClass().getClassLoader());
+      ClassLoader[] loaders = {beanClassClassLoader, 
fury.getClass().getClassLoader()};
+      if (codeGenerator == null) {
+        codeGenerator =
+            CodeGenerator.getSharedCodeGenerator(
+                beanClassClassLoader, fury.getClass().getClassLoader());
+        // Hold strong reference of {@link CodeGenerator}, so the referent of 
`DelayedRef`
+        // won't be null.
+        classResolver.setCodeGenerator(loaders, codeGenerator);
+      }
     }
     ClassLoader classLoader =
         codeGenerator.compile(
diff --git 
a/java/fury-core/src/main/java/org/apache/fury/codegen/CodeGenerator.java 
b/java/fury-core/src/main/java/org/apache/fury/codegen/CodeGenerator.java
index c6be7358..da3bc7e0 100644
--- a/java/fury-core/src/main/java/org/apache/fury/codegen/CodeGenerator.java
+++ b/java/fury-core/src/main/java/org/apache/fury/codegen/CodeGenerator.java
@@ -23,7 +23,6 @@ import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import java.lang.ref.SoftReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -41,6 +40,7 @@ import org.apache.fury.collection.Collections;
 import org.apache.fury.collection.MultiKeyWeakMap;
 import org.apache.fury.util.ClassLoaderUtils;
 import org.apache.fury.util.ClassLoaderUtils.ByteArrayClassLoader;
+import org.apache.fury.util.DelayedRef;
 import org.apache.fury.util.GraalvmSupport;
 import org.apache.fury.util.LoggerFactory;
 import org.apache.fury.util.Preconditions;
@@ -71,9 +71,9 @@ public class CodeGenerator {
   // FIXME The classloaders will only be reclaimed when the generated class 
are not be referenced.
   // FIXME CodeGenerator may reference to classloader, thus cause circular 
reference, neither can
   //  be gc.
-  private static final WeakHashMap<ClassLoader, SoftReference<CodeGenerator>> 
sharedCodeGenerator =
+  private static final WeakHashMap<ClassLoader, DelayedRef<CodeGenerator>> 
sharedCodeGenerator =
       new WeakHashMap<>();
-  private static final MultiKeyWeakMap<SoftReference<CodeGenerator>> 
sharedCodeGenerator2 =
+  private static final MultiKeyWeakMap<DelayedRef<CodeGenerator>> 
sharedCodeGenerator2 =
       new MultiKeyWeakMap<>();
 
   // use this package when bean class name starts with java.
@@ -301,11 +301,11 @@ public class CodeGenerator {
   }
 
   public static synchronized CodeGenerator 
getSharedCodeGenerator(ClassLoader... classLoaders) {
-    SoftReference<CodeGenerator> codeGeneratorWeakRef = 
sharedCodeGenerator2.get(classLoaders);
+    DelayedRef<CodeGenerator> codeGeneratorWeakRef = 
sharedCodeGenerator2.get(classLoaders);
     CodeGenerator codeGenerator = codeGeneratorWeakRef != null ? 
codeGeneratorWeakRef.get() : null;
     if (codeGenerator == null) {
       codeGenerator = new CodeGenerator(new 
ClassLoaderUtils.ComposedClassLoader(classLoaders));
-      sharedCodeGenerator2.put(classLoaders, new 
SoftReference<>(codeGenerator));
+      sharedCodeGenerator2.put(classLoaders, new DelayedRef<>(codeGenerator));
     }
     return codeGenerator;
   }
@@ -314,11 +314,11 @@ public class CodeGenerator {
     if (classLoader == null) {
       classLoader = CodeGenerator.class.getClassLoader();
     }
-    SoftReference<CodeGenerator> codeGeneratorWeakRef = 
sharedCodeGenerator.get(classLoader);
-    CodeGenerator codeGenerator = codeGeneratorWeakRef != null ? 
codeGeneratorWeakRef.get() : null;
+    DelayedRef<CodeGenerator> ref = sharedCodeGenerator.get(classLoader);
+    CodeGenerator codeGenerator = ref != null ? ref.get() : null;
     if (codeGenerator == null) {
       codeGenerator = new CodeGenerator(classLoader);
-      sharedCodeGenerator.put(classLoader, new SoftReference<>(codeGenerator));
+      sharedCodeGenerator.put(classLoader, new DelayedRef<>(codeGenerator));
     }
     return codeGenerator;
   }
diff --git 
a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java 
b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java
index e24e3f63..0e11568e 100644
--- a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java
+++ b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java
@@ -83,6 +83,7 @@ import org.apache.fury.annotation.Internal;
 import org.apache.fury.builder.CodecUtils;
 import org.apache.fury.builder.Generated;
 import org.apache.fury.builder.JITContext;
+import org.apache.fury.codegen.CodeGenerator;
 import org.apache.fury.codegen.Expression;
 import org.apache.fury.codegen.Expression.Invoke;
 import org.apache.fury.codegen.Expression.Literal;
@@ -245,6 +246,7 @@ public class ClassResolver {
         descriptorsCache = new ConcurrentHashMap<>();
     private ClassChecker classChecker = (classResolver, className) -> true;
     private GenericType objectGenericType;
+    private Map<List<ClassLoader>, CodeGenerator> codeGeneratorMap = new 
HashMap<>();
   }
 
   public ClassResolver(Fury fury) {
@@ -1796,6 +1798,20 @@ public class ClassResolver {
     return enumStringResolver;
   }
 
+  public CodeGenerator getCodeGenerator(ClassLoader... loaders) {
+    List<ClassLoader> loaderList = new ArrayList<>(loaders.length);
+    Collections.addAll(loaderList, loaders);
+    return extRegistry.codeGeneratorMap.get(loaderList);
+  }
+
+  public void setCodeGenerator(ClassLoader loader, CodeGenerator 
codeGenerator) {
+    setCodeGenerator(new ClassLoader[] {loader}, codeGenerator);
+  }
+
+  public void setCodeGenerator(ClassLoader[] loaders, CodeGenerator 
codeGenerator) {
+    extRegistry.codeGeneratorMap.put(Arrays.asList(loaders), codeGenerator);
+  }
+
   public Fury getFury() {
     return fury;
   }
diff --git a/java/fury-core/src/main/java/org/apache/fury/util/DelayedRef.java 
b/java/fury-core/src/main/java/org/apache/fury/util/DelayedRef.java
new file mode 100644
index 00000000..c2254ace
--- /dev/null
+++ b/java/fury-core/src/main/java/org/apache/fury/util/DelayedRef.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fury.util;
+
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+
+/**
+ * A weak reference which will have the longer lifetime of {@link 
WeakReference} and {@link
+ * SoftReference}.
+ *
+ * @param <T> type of the referent.
+ */
+public class DelayedRef<T> {
+  // If referent is strong reachable, this won't be null.
+  private final WeakReference<T> weakRef;
+  // If other components doesn't strong hold the referent, this is used
+  // to cache and get the referent. But the reference will be set to null
+  // when there is a memory pressure.
+  private final SoftReference<T> softRef;
+
+  public DelayedRef(T o) {
+    weakRef = new WeakReference<>(o);
+    softRef = new SoftReference<>(o);
+  }
+
+  public T get() {
+    T t = weakRef.get();
+    return t != null ? t : softRef.get();
+  }
+}
diff --git 
a/java/fury-core/src/test/java/org/apache/fury/codegen/CodeGeneratorTest.java 
b/java/fury-core/src/test/java/org/apache/fury/codegen/CodeGeneratorTest.java
index 1f27527b..e184112e 100644
--- 
a/java/fury-core/src/test/java/org/apache/fury/codegen/CodeGeneratorTest.java
+++ 
b/java/fury-core/src/test/java/org/apache/fury/codegen/CodeGeneratorTest.java
@@ -23,7 +23,6 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
-import java.lang.ref.SoftReference;
 import java.lang.reflect.Field;
 import java.util.HashMap;
 import java.util.Map;
@@ -39,13 +38,14 @@ import org.apache.fury.collection.MultiKeyWeakMap;
 import org.apache.fury.test.bean.Foo;
 import org.apache.fury.util.ClassLoaderUtils;
 import org.apache.fury.util.ClassLoaderUtils.ByteArrayClassLoader;
+import org.apache.fury.util.DelayedRef;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 @SuppressWarnings({"rawtypes", "unchecked"})
 public class CodeGeneratorTest {
-  private static WeakHashMap<ClassLoader, SoftReference<CodeGenerator>> 
sharedCodeGenerator;
-  private static MultiKeyWeakMap<SoftReference<CodeGenerator>> 
sharedCodeGenerator2;
+  private static WeakHashMap<ClassLoader, DelayedRef<CodeGenerator>> 
sharedCodeGenerator;
+  private static MultiKeyWeakMap<DelayedRef<CodeGenerator>> 
sharedCodeGenerator2;
 
   static {
     try {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to