From 95f868f2463ed9de814cff8dbd8f0f90965d4c63 Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Mon, 8 Jan 2018 10:14:33 +0100 Subject: [PATCH 1/2] Set context classloader to null in WorkQueue If threads created by the executor have a reference to the context classloader this may cause a leak. See: https://wiki.apache.org/tomcat/MemoryLeakProtection#cclThreadSpawnedByWebApp Bug: 517107 Change-Id: If79c1ebd3f9cf733fd88a5fce38fd46ff0d08d10 Signed-off-by: Matthias Sohn --- .../src/org/eclipse/jgit/lib/internal/WorkQueue.java | 1 + 1 file changed, 1 insertion(+) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/WorkQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/WorkQueue.java index e3f65ae7b..8f8aad52d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/WorkQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/WorkQueue.java @@ -71,6 +71,7 @@ public class WorkQueue { public Thread newThread(Runnable taskBody) { Thread thr = baseFactory.newThread(taskBody); thr.setName("JGit-WorkQueue"); //$NON-NLS-1$ + thr.setContextClassLoader(null); thr.setDaemon(true); return thr; } From 95fc2b210f3df3a1ff5c729dbc0624e3d25d34bd Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Mon, 8 Jan 2018 10:40:23 +0100 Subject: [PATCH 2/2] Don't subclass ThreadLocal to avoid memory leak in NLS These problem usually occur when you subclass ThreadLocal (usually to implement initialValue). Those classes reference the webapp's classloader. The ThreadLocal subclass in turn is referenced by each Thread instance (that's how ThreadLocals are implemented, they have a "helper-Map" in each Thread instance, so the leak is actually not a tiny Random instance but the whole webapp's classloader with a bunch of class definitions and statically referenced parts of the webapp. Bug: 449321 Change-Id: Ie7a8b0b90e40229e2471202f2a12637b9e0b1d11 Signed-off-by: Matthias Sohn --- .../src/org/eclipse/jgit/nls/NLS.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java b/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java index 3c0d839df..89a87af9e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java @@ -74,12 +74,7 @@ public class NLS { */ public static final Locale ROOT_LOCALE = new Locale("", "", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - private static final InheritableThreadLocal local = new InheritableThreadLocal() { - @Override - protected NLS initialValue() { - return new NLS(Locale.getDefault()); - } - }; + private static final InheritableThreadLocal local = new InheritableThreadLocal<>(); /** * Sets the locale for the calling thread. @@ -99,10 +94,19 @@ public class NLS { /** * Sets the JVM default locale as the locale for the calling thread. *

- * Semantically this is equivalent to NLS.setLocale(Locale.getDefault()). + * Semantically this is equivalent to + * NLS.setLocale(Locale.getDefault()). */ public static void useJVMDefaultLocale() { - local.set(new NLS(Locale.getDefault())); + useJVMDefaultInternal(); + } + + // TODO(ms): change signature of public useJVMDefaultLocale() in 5.0 to get + // rid of this internal method + private static NLS useJVMDefaultInternal() { + NLS b = new NLS(Locale.getDefault()); + local.set(b); + return b; } /** @@ -122,7 +126,11 @@ public class NLS { * {@link org.eclipse.jgit.errors.TranslationStringMissingException} */ public static T getBundleFor(Class type) { - return local.get().get(type); + NLS b = local.get(); + if (b == null) { + b = useJVMDefaultInternal(); + } + return b.get(type); } final private Locale locale;