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

rec pushed a commit to branch 
refactoring/435-Improve-performance-of-ImportResolver
in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git


The following commit(s) were added to 
refs/heads/refactoring/435-Improve-performance-of-ImportResolver by this push:
     new 282b9bf26 Issue #435: Improve performance of ImportResolver
282b9bf26 is described below

commit 282b9bf264f2abb729bc47b77575141570254ecc
Author: Richard Eckart de Castilho <r...@apache.org>
AuthorDate: Wed Jan 22 17:19:17 2025 +0100

    Issue #435: Improve performance of ImportResolver
    
    - Introduce a cache for services loaded per classloader to avoid expensive 
repeated loading of services via a ServiceLoader
---
 .../uima/internal/util/ServiceLoaderUtil.java      | 32 ++++++++++++++++++++--
 .../apache/uima/cas/impl/TypeSystemImplTest.java   |  8 ++++--
 2 files changed, 34 insertions(+), 6 deletions(-)

diff --git 
a/uimaj-core/src/main/java/org/apache/uima/internal/util/ServiceLoaderUtil.java 
b/uimaj-core/src/main/java/org/apache/uima/internal/util/ServiceLoaderUtil.java
index 7ffceaef0..f2820db6f 100644
--- 
a/uimaj-core/src/main/java/org/apache/uima/internal/util/ServiceLoaderUtil.java
+++ 
b/uimaj-core/src/main/java/org/apache/uima/internal/util/ServiceLoaderUtil.java
@@ -21,6 +21,9 @@ package org.apache.uima.internal.util;
 import java.lang.invoke.MethodHandles;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.ServiceConfigurationError;
 import java.util.ServiceLoader;
 import java.util.Spliterator;
@@ -35,6 +38,9 @@ public class ServiceLoaderUtil {
   private static final Logger LOG = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private static final int MAX_BROKEN_SERVICES = 16;
 
+  private static final WeakIdentityMap<ClassLoader, Map<Class<?>, List<?>>> 
cl_to_services = //
+          WeakIdentityMap.newHashMap();
+
   public static <T> Stream<T> loadServicesSafely(Class<T> aService) {
     var cl = ClassLoaderUtils.findClassLoader();
     return loadServicesSafely(aService, cl, null);
@@ -46,9 +52,29 @@ public class ServiceLoaderUtil {
 
   static <T> Stream<T> loadServicesSafely(Class<T> aService, ClassLoader 
aClassLoader,
           Collection<Throwable> aErrorCollector) {
-    var loader = ServiceLoader.load(aService, aClassLoader);
-    return StreamSupport.stream(
-            new ServiceLoaderSpliterator<T>(aService, loader.iterator(), 
aErrorCollector), false);
+
+    Map<Class<?>, List<?>> servicesMap;
+
+    synchronized (cl_to_services) {
+      servicesMap = cl_to_services.get(aClassLoader);
+      if (servicesMap == null) {
+        servicesMap = new LinkedHashMap<>();
+        cl_to_services.put(aClassLoader, servicesMap);
+      }
+    }
+
+    synchronized (servicesMap) {
+      @SuppressWarnings("unchecked")
+      var services = (List<T>) servicesMap.get(aService);
+      if (services == null) {
+        var loader = ServiceLoader.load(aService, aClassLoader);
+        services = StreamSupport.stream(
+                new ServiceLoaderSpliterator<T>(aService, loader.iterator(), 
aErrorCollector),
+                false).toList();
+        servicesMap.put(aService, services);
+      }
+      return services.stream();
+    }
   }
 
   private static class ServiceLoaderSpliterator<T> implements Spliterator<T> {
diff --git 
a/uimaj-core/src/test/java/org/apache/uima/cas/impl/TypeSystemImplTest.java 
b/uimaj-core/src/test/java/org/apache/uima/cas/impl/TypeSystemImplTest.java
index a6edee853..e8e5c8fbe 100644
--- a/uimaj-core/src/test/java/org/apache/uima/cas/impl/TypeSystemImplTest.java
+++ b/uimaj-core/src/test/java/org/apache/uima/cas/impl/TypeSystemImplTest.java
@@ -118,11 +118,12 @@ class TypeSystemImplTest {
     LOG.info("Metaspace exhaustion test starting - this may take a while...");
     var startTime = currentTimeMillis();
     var target = threshold * 2;
+    var loadedClasses = classLoadingMXBean.getLoadedClassCount();
     for (var i = 0; i < threshold * 2; i++) {
       if ((i + 1) % 250 == 0) {
         var duration = currentTimeMillis() - startTime;
-        LOG.info("Metaspace exhaustion test - progress: {} / {} -- {}ms per 
CAS", i + 1, target,
-                duration / i + 1);
+        LOG.info("Metaspace exhaustion test - progress: {} / {} -- {}ms per 
CAS - {} classes",
+                i + 1, target, duration / i + 1, (loadedClasses - 
classesLoadedAtStart));
       }
 
       var resMgr = new ResourceManager_impl();
@@ -132,7 +133,8 @@ class TypeSystemImplTest {
       // Make sure the consolidated type system is evicted from the weak 
hashmap cache
       System.gc();
 
-      assertThat(classLoadingMXBean.getLoadedClassCount()) //
+      loadedClasses = classLoadingMXBean.getLoadedClassCount();
+      assertThat(loadedClasses) //
               .as("High number of new loaded classes during test indicates 
leak")
               .isLessThan(classesLoadedAtStart + threshold);
     }

Reply via email to