今天我得到PermGen OutOfMemory
错误。
分析表明,Nearest GC Root for WebappClassLoader
is Logback thread:
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)
我使用具有热重新部署功能的 Tomcat 8并通过以下方式进行reloadable="true"
外部化:CLASSPATH
PreResources
<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.xml
与scan="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"
线程?
我发现了一些相关的讨论,但无法理解如何处理该信息:
- http://logback.10977.n7.nabble.com/How-to-stop-all-appenders-td3023.html
- 使用 logback 关闭时是否需要刷新事件?
- 停止自定义 logback 异步附加程序的正确方法
- 停止 Logback 系统以彻底关闭
更新我玩堆转储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
通过在 Logback 源中搜索,ExecutorServiceUtil
我找到了更改日志条目:
ch.qos.logback.core.util.ExecutorServiceUtil#THREAD_FACTORY 打开的所有线程现在都是守护进程,它修复了在未调用 LoggerContext#stop() 时应用程序在关闭时挂起的问题 (LOGBACK-929)。请注意,守护线程会被 JVM 突然终止,这可能会导致不良结果,例如 FileAppender 写入的文件损坏。仍然强烈建议应用程序调用 LoggerContext#stop() (例如,在关闭挂钩中)以正常关闭附加程序。
容器环境中的守护线程是危险的并导致内存泄漏,这对吗?