在Tomcat中多次重新部署Web应用程序后,如何解决Metaspace OOM?

时间:2019-06-14 02:34:11

标签: java out-of-memory tomcat8 metaspace

全部,

我正在使用openjdk 1.8.0_212-b04,Tomcat 8.0.21和Red Hat 6.4。

并且我已经调整了测试Web应用程序,请确保在重新部署后不会出现没有此类消息

WARNING: The web application [Test] appears to have started a thread named [test-job_Worker-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread

这是我在测试中使用的GC参数:

-Xms2G -Xmx4G 
-XX:MaxMetaspaceSize=1G -XX:CompressedClassSpaceSize=300 

-Dsun.rmi.dgc.client.gcInterval=9223372036854775807 
-Dsun.rmi.dgc.server.gcInterval=9223372036854775807 
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:GCLogFile Size=10M -XX:NumberOfGCLogFiles=5 -Xloggc:$LOG_HOME/gc-$START_TIME.log

然后我使用了一个shell脚本来保持联系web.xml以重新部署该Web应用程序。测试期间没有其他流量/操作。

这是测试期间VisualVM的元空间图: enter image description here

如图所示:在第1点,元空间会在某个时候缩小,因此我认为应该不存在元空间内存泄漏。

但是在第2点,Tomcat抛出了元空间OOM:

14-Jun-2019 09:26:32.184 SEVERE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run Unexpected death of background thread ContainerBackgroundProcessor[StandardEngine[Catalina]]
 java.lang.OutOfMemoryError: Metaspace
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2472)
        at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:854)
        at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1274)
        at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1157)
        at org.apache.logging.log4j.status.StatusLogger.<init>(StatusLogger.java:108)
        at org.apache.logging.log4j.status.StatusLogger.<clinit>(StatusLogger.java:85)
        at org.apache.logging.log4j.web.Log4jServletContextListener.<clinit>(Log4jServletContextListener.java:44)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

在第3点,我单击了VisualVM中的GC按钮,元空间实际上是可收缩的。

我尝试添加参数-XX:MaxMetaspaceFreeRatio = 60,但仍然得到了smae结果。

这是GCeasy的元空间图: enter image description here

这是GC日志: GC Log

感谢您提供解决OOM错误/排除故障/调试的建议。

更新1: 感谢@samabcde的建议,我添加了se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor来进行更多测试。 它有助于删除一些ThreadLocal变量,并使Web应用程序保留更多时间。

14-Jun-2019 17:44:31.463 INFO [ContainerBackgroundProcessor[StandardEngine[Catalina]]] se.jiderhamn.classloader.leak.prevention.JULLogger.info Custom ThreadLocal of type org.springframework.core.NamedThreadLocal: Prototype beans currently in creation with value null will be remove()d from Thread[ContainerBackgroundProcessor[StandardEngine[Catalina]],5,main]

但是仍然获得了Metaspace OOM。 enter image description here

1 个答案:

答案 0 :(得分:3)

在测试期间,我看到Metaspace几乎达到最大值,然后我停下来重新部署Web应用程序,并进行了堆转储,将其置于MAT下进行分析。

正如预期的那样,有许多WebappClassLoader。 但是我检查了它是 GC根目录的路径|排除所有相位/弱/软。等引用,这是GC根目录。

我检查了它是 GC根目录的路径|包含所有引用,那么GC根就不止一个。 enter image description here

所以...这些WebappClassLoader实际上是phatom / weak / soft。被其他对象引用时,它将留在堆中一段时间​​才能被释放。

然后我添加GC参数:-XX:SoftRefLRUPolicyMSPerMB = 10,可以将Web应用程序重新部署超过1000次...

问题就解决了!

PS。因为我们不会在生产环境中在短时间内重新部署Web应用程序,所以我们不会使用-XX:SoftRefLRUPolicyMSPerMB = 10。