如何摆脱由休眠线程引起的 tomcat 内存泄漏日志条目
How to get rid of tomcat memory leak log entries caused by a sleeping thread
知道如何删除以下 Tomcat 日志条目:
SEVERE: The web application [/my-app] appears to have started a thread named [Thread-11] but has failed to stop it. This is very likely to create a memory leak.
Jun 29, 2015 11:14:33 AM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
我正在关闭 tomcat 7.0.42.
线程转储如下所示:
"Thread-11" daemon prio=10 tid=0x00007fedc0bd5000 nid=0x2983 waiting on condition [0x00007fedbacef000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.my.app.Test$WorkerThread.run(Test.java:248)
java 第 248 行是:sleep(1000*60*pollIntervalMinutes);
public void start()
{
WorkerThread t= new WorkerThread(this);
t.setDaemon(true);
t.start();
}
private class WorkerThread extends Thread
{
Controller controller=null;
int pollIntervalMinutes=0;
private boolean alive = true;
public WorkerThread(Controller controller)
{
this.controller=controller;
pollIntervalMinutes=60;
}
@Override
public void run()
{
while(alive)
{
try
{
sleep(1000*60*pollIntervalMinutes);
controller.createAllProjectsIfNeeded();
}
catch (Exception e)
{
alive = false;
}
}
}
}
谢谢
问题似乎是 Tomcat 没有尝试停止应用程序创建的非托管线程。
查找报告错误的 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
的实现,JVM 线程从清理过程中跳过。
因此,我会说永远不会抛出 InterruptedException,因此 alive 不会像预期的那样设置为 false。
@SuppressWarnings("deprecation")
private void clearReferencesThreads() {
Thread[] threads = getThreads();
// Iterate over the set of threads
for (Thread thread : threads) {
if (thread != null) {
ClassLoader ccl = thread.getContextClassLoader();
if (ccl == this) {
// Don't warn about this thread
if (thread == Thread.currentThread()) {
continue;
}
// JVM controlled threads
ThreadGroup tg = thread.getThreadGroup();
if (tg != null &&
JVM_THREAD_GROUP_NAMES.contains(tg.getName())) {
// HttpClient keep-alive threads
if (clearReferencesHttpClientKeepAliveThread &&
thread.getName().equals("Keep-Alive-Timer")) {
thread.setContextClassLoader(parent);
log.debug(sm.getString(
"webappClassLoader.checkThreadsHttpClient"));
}
// Don't warn about remaining JVM controlled threads
continue;
}
// Skip threads that have already died
if (!thread.isAlive()) {
continue;
}
// TimerThread can be stopped safely so treat separately
if (thread.getClass().getName().equals(
"java.util.TimerThread") &&
clearReferencesStopTimerThreads) {
clearReferencesStopTimerThread(thread);
continue;
}
if (isRequestThread(thread)) {
log.error(sm.getString("webappClassLoader.warnRequestThread",
contextName, thread.getName()));
} else {
log.error(sm.getString("webappClassLoader.warnThread",
contextName, thread.getName()));
}
// Don't try an stop the threads unless explicitly
// configured to do so
if (!clearReferencesStopThreads) {
continue;
}
// If the thread has been started via an executor, try
// shutting down the executor
try {
Field targetField =
thread.getClass().getDeclaredField("target");
targetField.setAccessible(true);
Object target = targetField.get(thread);
if (target != null &&
target.getClass().getCanonicalName().equals(
"java.util.concurrent.ThreadPoolExecutor.Worker")) {
Field executorField =
target.getClass().getDeclaredField("this[=10=]");
executorField.setAccessible(true);
Object executor = executorField.get(target);
if (executor instanceof ThreadPoolExecutor) {
((ThreadPoolExecutor) executor).shutdownNow();
}
}
} catch (SecurityException e) {
log.warn(sm.getString(
"webappClassLoader.stopThreadFail",
thread.getName(), contextName), e);
} catch (NoSuchFieldException e) {
log.warn(sm.getString(
"webappClassLoader.stopThreadFail",
thread.getName(), contextName), e);
} catch (IllegalArgumentException e) {
log.warn(sm.getString(
"webappClassLoader.stopThreadFail",
thread.getName(), contextName), e);
} catch (IllegalAccessException e) {
log.warn(sm.getString(
"webappClassLoader.stopThreadFail",
thread.getName(), contextName), e);
}
// This method is deprecated and for good reason. This is
// very risky code but is the only option at this point.
// A *very* good reason for apps to do this clean-up
// themselves.
thread.stop();
}
}
}
}
我可以想到三个选项来避免这种内存泄漏:
- 提供关闭钩子作为了解您生成的线程并可以中断它们的线程
- 不要生成会在 while 循环中无限旋转的线程,以便它们可以在完成处理后停止
在 ExecutorService
中包装线程并在应用程序关闭事件时调用 shutdownNow()
。
这可以通过使用 @WebListener
注释的服务 class 来实现
并在方法中调用 shutdownNow()
:contextDestroyed(ServletContextEvent event)
.
shutdownNow()
Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.
This method does not wait for actively executing tasks to terminate. Use awaitTermination
to do that.
There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via Thread.interrupt()
, so any task that fails to respond to interrupts may never terminate.
我建议您将 ServletContextListener 添加到您的上下文 (example) 中,它与您的线程通信并在上下文被销毁时停止它。这样当您的应用程序被卸载时(如果您删除 .war 或替换它)线程停止并启动一个新的线程。
知道如何删除以下 Tomcat 日志条目:
SEVERE: The web application [/my-app] appears to have started a thread named [Thread-11] but has failed to stop it. This is very likely to create a memory leak.
Jun 29, 2015 11:14:33 AM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
我正在关闭 tomcat 7.0.42.
线程转储如下所示:
"Thread-11" daemon prio=10 tid=0x00007fedc0bd5000 nid=0x2983 waiting on condition [0x00007fedbacef000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.my.app.Test$WorkerThread.run(Test.java:248)
java 第 248 行是:sleep(1000*60*pollIntervalMinutes);
public void start()
{
WorkerThread t= new WorkerThread(this);
t.setDaemon(true);
t.start();
}
private class WorkerThread extends Thread
{
Controller controller=null;
int pollIntervalMinutes=0;
private boolean alive = true;
public WorkerThread(Controller controller)
{
this.controller=controller;
pollIntervalMinutes=60;
}
@Override
public void run()
{
while(alive)
{
try
{
sleep(1000*60*pollIntervalMinutes);
controller.createAllProjectsIfNeeded();
}
catch (Exception e)
{
alive = false;
}
}
}
}
谢谢
问题似乎是 Tomcat 没有尝试停止应用程序创建的非托管线程。
查找报告错误的 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
的实现,JVM 线程从清理过程中跳过。
因此,我会说永远不会抛出 InterruptedException,因此 alive 不会像预期的那样设置为 false。
@SuppressWarnings("deprecation")
private void clearReferencesThreads() {
Thread[] threads = getThreads();
// Iterate over the set of threads
for (Thread thread : threads) {
if (thread != null) {
ClassLoader ccl = thread.getContextClassLoader();
if (ccl == this) {
// Don't warn about this thread
if (thread == Thread.currentThread()) {
continue;
}
// JVM controlled threads
ThreadGroup tg = thread.getThreadGroup();
if (tg != null &&
JVM_THREAD_GROUP_NAMES.contains(tg.getName())) {
// HttpClient keep-alive threads
if (clearReferencesHttpClientKeepAliveThread &&
thread.getName().equals("Keep-Alive-Timer")) {
thread.setContextClassLoader(parent);
log.debug(sm.getString(
"webappClassLoader.checkThreadsHttpClient"));
}
// Don't warn about remaining JVM controlled threads
continue;
}
// Skip threads that have already died
if (!thread.isAlive()) {
continue;
}
// TimerThread can be stopped safely so treat separately
if (thread.getClass().getName().equals(
"java.util.TimerThread") &&
clearReferencesStopTimerThreads) {
clearReferencesStopTimerThread(thread);
continue;
}
if (isRequestThread(thread)) {
log.error(sm.getString("webappClassLoader.warnRequestThread",
contextName, thread.getName()));
} else {
log.error(sm.getString("webappClassLoader.warnThread",
contextName, thread.getName()));
}
// Don't try an stop the threads unless explicitly
// configured to do so
if (!clearReferencesStopThreads) {
continue;
}
// If the thread has been started via an executor, try
// shutting down the executor
try {
Field targetField =
thread.getClass().getDeclaredField("target");
targetField.setAccessible(true);
Object target = targetField.get(thread);
if (target != null &&
target.getClass().getCanonicalName().equals(
"java.util.concurrent.ThreadPoolExecutor.Worker")) {
Field executorField =
target.getClass().getDeclaredField("this[=10=]");
executorField.setAccessible(true);
Object executor = executorField.get(target);
if (executor instanceof ThreadPoolExecutor) {
((ThreadPoolExecutor) executor).shutdownNow();
}
}
} catch (SecurityException e) {
log.warn(sm.getString(
"webappClassLoader.stopThreadFail",
thread.getName(), contextName), e);
} catch (NoSuchFieldException e) {
log.warn(sm.getString(
"webappClassLoader.stopThreadFail",
thread.getName(), contextName), e);
} catch (IllegalArgumentException e) {
log.warn(sm.getString(
"webappClassLoader.stopThreadFail",
thread.getName(), contextName), e);
} catch (IllegalAccessException e) {
log.warn(sm.getString(
"webappClassLoader.stopThreadFail",
thread.getName(), contextName), e);
}
// This method is deprecated and for good reason. This is
// very risky code but is the only option at this point.
// A *very* good reason for apps to do this clean-up
// themselves.
thread.stop();
}
}
}
}
我可以想到三个选项来避免这种内存泄漏:
- 提供关闭钩子作为了解您生成的线程并可以中断它们的线程
- 不要生成会在 while 循环中无限旋转的线程,以便它们可以在完成处理后停止
在
ExecutorService
中包装线程并在应用程序关闭事件时调用shutdownNow()
。这可以通过使用
@WebListener
注释的服务 class 来实现 并在方法中调用shutdownNow()
:contextDestroyed(ServletContextEvent event)
.
shutdownNow()
Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution. This method does not wait for actively executing tasks to terminate. Use
awaitTermination
to do that.There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via
Thread.interrupt()
, so any task that fails to respond to interrupts may never terminate.
我建议您将 ServletContextListener 添加到您的上下文 (example) 中,它与您的线程通信并在上下文被销毁时停止它。这样当您的应用程序被卸载时(如果您删除 .war 或替换它)线程停止并启动一个新的线程。