We have a webapp that uses on average 20% CPU when idle, with no network traffic or any kind of requests.
It is running on Java 11, Tomcat 9, Spring Framework 5.3, Hibernate 5.4.
However the issues I will describe below were true on Java 8, Tomcat 8.5, Spring 4.3 and Hibernate 4. as well.
I tried to profile the application using JFR and JMC, and I experimented with a lot of configurations.
In the image above it looks like catalina-utility-1 and catalina-utility-2 threads wake up periodically and for a few seconds use a lot of CPU.
Also there seems to be a huge amount of memory allocations done by these threads, 30+ GB in total in the sampled 5 minutes interval.
对于此分析,我已将 JFR 配置为最多记录所有内容,并启用所有选项。
![enter image description here](https://i.stack.imgur.com/jLOSq.png)
当我尝试通过查看方法分析详细信息来更深入地挖掘细节时,我发现它似乎与org.apache.catalina.webresources.Cache.getResource()
.
所以我开始阅读有关 Tomcat 缓存的内容,并尝试通过不同的参数来调整它context.xml
像这样的文件:
<Context>
<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<!--
<Manager pathname="" />
-->
<Resources cachingAllowed="true" cacheMaxSize="3024000" cacheObjectMaxSize="10240" cacheTtl="10000"/>
</Context>
在这个用于 JFR 分析的特定示例中,我将缓存大小增加到 3GB,并且cacheTtl
至 10 秒。我认为更大的缓存和更大的 TTL 会影响 CPU 峰值的间隔,因为我怀疑 Tomcat 默认每 5 秒检查一次缓存(最初大小为 1G)。
然而,无论我为缓存大小或 ttl 设置什么值,周期性 CPU 峰值都是相同的。
而且缓存大小足以容纳 Tomcat 想要放入其中的任何内容,因为在日志中看到警告后我增加了该值。无论如何,1GB 足以摆脱警告。
我还尝试了 1 到 5GB 的堆大小,上面的分析是使用 5GB 的堆大小完成的。如果不开始达到物理内存限制,我就无法真正超过这个值。
从 Java 8 天开始,我们就使用 G1GC 作为垃圾收集器。调整其参数不会影响 CPU 使用率。
我还尝试了 ParallelGC 和 SerialGC,但 CPU 使用模式保持不变。
在谷歌上搜索此类问题没有结果,我完全陷入了我还可以尝试什么或者我应该看什么的问题。
欢迎任何建议。谢谢。
更新1:
看来我最初遇到了格式问题,并且打开<context>
标签缺失context.xml
当解析时。修复。
我也尝试过,按照建议<Context reloadable="false">
以便可重新加载显式设置为 false。完全没有效果。
是否可以从其他地方设置可重新加载标志?我猜测也许其他一些文件或设置会应用它,即使在context.xml
它被设置为false
.