This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit be639f85e902a70990d3e4880dbc04e800a5892d Author: opencode <[email protected]> AuthorDate: Wed Sep 30 13:27:25 2026 +0200 Synchronise access to the StandardHost childClassLoaders map which is written by context start threads and read by findReloadedContextMemoryLeaks() from Manager/JMX threads, preventing concurrent modification of the underlying WeakHashMap during iteration --- java/org/apache/catalina/core/StandardHost.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/java/org/apache/catalina/core/StandardHost.java b/java/org/apache/catalina/core/StandardHost.java index 86fff7b482..06f215c58e 100644 --- a/java/org/apache/catalina/core/StandardHost.java +++ b/java/org/apache/catalina/core/StandardHost.java @@ -169,6 +169,12 @@ public class StandardHost extends ContainerBase implements Host { */ private final Map<ClassLoader,String> childClassLoaders = new WeakHashMap<>(); + /** + * Lock used to synchronise access to {@link #childClassLoaders} which is written when child contexts start and + * read when memory leaks are checked. + */ + private final Object childClassLoadersLock = new Object(); + /** * Any file or directory in {@link #appBase} that this pattern matches will be ignored by the automatic deployment @@ -688,8 +694,10 @@ public class StandardHost extends ContainerBase implements Host { public void lifecycleEvent(LifecycleEvent event) { if (event.getType().equals(AFTER_START_EVENT)) { if (event.getSource() instanceof Context context) { - childClassLoaders.put(context.getLoader().getClassLoader(), - context.getServletContext().getContextPath()); + synchronized (childClassLoadersLock) { + childClassLoaders.put(context.getLoader().getClassLoader(), + context.getServletContext().getContextPath()); + } } } } @@ -709,7 +717,12 @@ public class StandardHost extends ContainerBase implements Host { List<String> result = new ArrayList<>(); - for (Map.Entry<ClassLoader,String> entry : childClassLoaders.entrySet()) { + List<Map.Entry<ClassLoader,String>> entries; + synchronized (childClassLoadersLock) { + entries = new ArrayList<>(childClassLoaders.entrySet()); + } + + for (Map.Entry<ClassLoader,String> entry : entries) { ClassLoader cl = entry.getKey(); if (cl instanceof WebappClassLoaderBase) { if (!((WebappClassLoaderBase) cl).getState().isAvailable()) { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
