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); }