diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/DataStructures.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/DataStructures.java
new file mode 100644
index 0000000000..f80928901e
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/DataStructures.java
@@ -0,0 +1,48 @@
+/*-
+ * #%L
+ * Elastic APM Java agent
+ * %%
+ * Copyright (C) 2018 - 2019 Elastic and contributors
+ * %%
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. 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.
+ * #L%
+ */
+package co.elastic.apm.agent.util;
+
+import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap;
+
+public class DataStructures {
+
+ /**
+ * Use this utility method for WeakConcurrentMap creation if it is created in the context of a request processing
+ * thread whose context class loader is the web application ClassLoader.
+ * This leaks the web application ClassLoader when the application is undeployed/redeployed.
+ *
+ * Tomcat will then stop the thread because it thinks it was created by the web application.
+ * That means that the map will never be cleared, creating a severe memory leak.
+ *
+ * @param map key type
+ * @param map value type
+ * @return a new WeakConcurrentMap with a cleaner thread who's context class loader is the system/bootstrap class loader
+ */
+ public static WeakConcurrentMap createWeakConcurrentMapWithCleanerThread() {
+ WeakConcurrentMap map = new WeakConcurrentMap<>(true);
+ map.getCleanerThread().setContextClassLoader(null);
+ return map;
+ }
+}
diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java
index 645f962405..29f736bfba 100644
--- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java
+++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java
@@ -27,6 +27,7 @@
import co.elastic.apm.agent.bci.VisibleForAdvice;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.TraceContextHolder;
+import co.elastic.apm.agent.util.DataStructures;
import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap;
import javax.annotation.Nullable;
@@ -35,17 +36,7 @@
public abstract class JdbcHelper {
@SuppressWarnings("WeakerAccess")
@VisibleForAdvice
- public static final WeakConcurrentMap