gpt4 book ai didi

tomcat - 在容器环境中优雅地停止 Logback

转载 作者:行者123 更新时间:2023-11-28 21:47:44 25 4
gpt4 key购买 nike

今天我收到 PermGen OutOfMemory 错误。

分析表明 最近的 GC Root WebappClassLoader 是 Logback 线程:

this     - value: org.apache.catalina.loader.WebappClassLoader #4
<- contextClassLoader (thread object) - class: java.lang.Thread, value: org.apache.catalina.loader.WebappClassLoader #4

即:

java.lang.Thread#11 - logback-1

此线程的堆转储中的线程转储:

"logback-1" daemon prio=5 tid=34 WAITING
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:458)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:359)
Local Variable: java.util.concurrent.SynchronousQueue$TransferStack$SNode#1
Local Variable: java.util.concurrent.SynchronousQueue$TransferStack#6
at java.util.concurrent.SynchronousQueue.take(SynchronousQueue.java:925)
Local Variable: java.util.concurrent.SynchronousQueue#6
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
Local Variable: java.util.concurrent.ThreadPoolExecutor#34
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
Local Variable: java.util.concurrent.ThreadPoolExecutor$Worker#11
at java.lang.Thread.run(Thread.java:745)

我使用具有热重新部署功能 reloadable="true" 并通过 PreResources 外部化 CLASSPATH 的 Tomcat 8:

<Context docBase="/home/user/devel/app/src/main/webapp"
reloadable="true">
<Resources>
<!-- To override application.properties and logback.xml -->
<PreResources className="org.apache.catalina.webresources.DirResourceSet"
base="/home/user/devel/app/.config"
internalPath="/"
webAppMount="/WEB-INF/classes" />
</Resources>
</Context>

logback.xmlscan="true":

<configuration debug="false" scan="true" scanPeriod="5 seconds">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
...

/home/user/devel/app/.config/logback.xml 中保存修改后 Tomcat 8 收到通知(我不确定使用什么 API 来监视 fs 上的更改)和应用程序重新部署开始了。这就是 PermGen OutOfMemory 之前发生的情况。

容器环境下如何优雅停止Logback?

如何停止"logback-1"线程?

我找到了一些相关的讨论,但不明白如何处理这些信息:

更新 我在 visualvm 中使用堆转储。在错误的 logback-1 线程的引用跳转下:

lvl1 = flatten(filter(referees(heap.findObject(0xf4c77610)), "!/WebappClassLoader/(classof(it).name)"))

lvl2 = flatten(map(lvl1, "referees(it)"))

lvl3 = flatten(map(lvl2, "referees(it)"))

它指的是

ch.qos.logback.core.util.ExecutorServiceUtil$1

通过在 ExecutorServiceUtil 的 Logback 源中进行 grepping I found changelog entry :

All threads opened by ch.qos.logback.core.util.ExecutorServiceUtil#THREAD_FACTORY are now daemons, which fixes an application hang on shutdown when LoggerContext#stop() is not called (LOGBACK-929). Note that daemon threads are abruptly terminated by the JVM, which may cause undesirable results, such as corrupted files written by the FileAppender. It is still highly recommended for the application to call LoggerContext#stop() (e.g., in a shutdown hook) to gracefully shutdown appenders.

在容器环境中,守护线程是危险的并会导致内存泄漏,对吗?

最佳答案

我不完全明白我应该做什么。目前,我从项目 pom.xml 和这一行中删除了 jul-to-slf4j 桥:

<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>

来自 logback.xml。即使使用这一行,应用程序也没有 "logback-1" 线程。

As suggest official docs我注册:

public class ShutdownCleanupListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) { }

@Override
public void contextDestroyed(ServletContextEvent sce) {
if (LoggerFactory.getILoggerFactory() instanceof LoggerContext) {
((LoggerContext) LoggerFactory.getILoggerFactory() ).stop();
}
}
}

web.xml 中:

<listener>
<listener-class>com.app.servlet.ShutdownCleanupListener</listener-class>
</listener>

移除直接依赖于:

import ch.qos.logback.classic.LoggerContext;

可以使用反射。

不确定我做的是否正确。我将查看是否由于 Logback 而出现 PermGen OutOfMemory 错误。

更新 在我发现来自 ExecutorServiceUtil 类的引用依赖项后,我检查了 Logback 源,发现该类创建的线程名称如上面的 bad:

 thread.setName("logback-" + threadNumber.getAndIncrement());

该类仅在ch.qos.logback.core.ContextBase中使用,线程在里面倾斜:

public void stop() {
// We don't check "started" here, because the executor service uses
// lazy initialization, rather than being created in the start method
stopExecutorService();
started = false;
}

请注意,LoggerContextContextBase 的子类,因此上述解决方案真正解决了我的问题

关于tomcat - 在容器环境中优雅地停止 Logback,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34493817/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com