Tomcat部署WebSocket应用后内存逐渐耗尽

20

我在一个AWS盒子上运行Tomcat 8.5.9,部署了10个不同的WebSocket应用程序,每个应用程序基本上都充当消息代理。https连接器使用Http11NioProtocol协议。我设置的唯一参数是maxThreads = 200以及证书信息。

请求量不是很大。自周一早上以来一直在运行,这是管理器状态的情况:

最大线程数:200
当前线程数:38
当前忙碌线程数:0
保持的套接字数:1
最长处理时间:234毫秒
处理时间:17.254秒
请求计数:33351
错误计数:325
接收字节数:0.00 MB
发送字节数:34.07 MB

几天后,我注意到内存使用量继续增长。每两周左右,我必须重新启动Tomcat服务,以防止出现OutOfMemoryException。

我一直在进行堆转储并使用Eclipse MAT进行分析,它总是指向WsFrameServer类作为问题嫌疑对象。最近的dump显示如下:

"org.apache.tomcat.websocket.server.WsFrameServer"的5,146个实例,
被"java.net.URLClassLoader @ 0x6c0047c28"加载,占用了1,383,143,200
(73.13%)字节。这些实例是从一个“java.util.concurrent.ConcurrentHashMap $ Node []”的实例引用的。

Dominator Tree当前有106,000个条目,其中大多数是WsFrameServer类。

我做错了什么还是这个情况“正常”?是否应该在Tomcat或Connector上设置特定的设置以防止此情况发生?

提前致谢。

编辑:我不确定这是否有帮助,但这是VisualVM监视器的样子:

VisualVM 监控器

3个回答

5
很难确定没有更多的细节,但这可能与您的会话保留有关。我认为正在发生的是WsFrameServer扩展了WsFrameBase被添加到会话中。
如果您有无限的会话保留策略,那么最终将耗尽内存。

尝试设置非0 sessionTimeout


我的conf\web.xml文件指定了以下内容:<session-config> <session-timeout>30</session-timeout> </session-config> 在webapp/WEB-INF/web.xml中也没有覆盖这个设置。我需要在其他地方指定吗? - Tommo
使用堆转储(heap dump)尝试查看哪个类保存了对持有所有WsFrameServer的ConcurrentHashMap的引用,这应该会更清楚地解决问题。另外,您是否正在使用持久化会话管理器? - Magnus
我目前没有使用持久会话管理器。现在正在检查Eclipse MAT,以查找持有对ConcurrentHashMap的引用的类。 - Tommo
@Tommo,那么实际的解决方案是什么?你更新了哪些属性? - Mark Bramnik

0

你的问题中缺少代码(特别是如何管理websocket连接)。

你是否在异步模式下使用了tomcat,并在某个地方使用了连接列表?

你不要忘记将关闭和错误事件绑定到一个代码,以从列表中删除有问题的连接。


我不确定您的问题是什么。据我所知,我没有在异步模式下使用tomcat。我正在维护一个包含对话引用的数据结构,在关闭事件期间从数据结构中删除这些会话。目前,在错误事件期间我并没有删除这些会话。 - Tommo
很难解释。例如,使用SSE(与websocket相似)https://golb.hplar.ch/p/Server-Sent-Events-with-Spring,您可以使用emitter.onCompletion(() -> this.emitters.remove(emitter)); emitter.onTimeout(() -> this.emitters.remove(emitter));来管理websocket / SSE发射器。如果您使用会话存储websocket,那么为什么要使用websocket?会话意味着您已经处于用户上下文中,因此发送数据,无需使用websocket。 - wargre

0

众所周知,Java GC是懒惰的。它的内存将继续增长,直到没有更多的内存可用,然后会触发GC来收集垃圾。

从您的VisualVM截图中,我们可以看到内存使用情况相对正常:随着时间的推移,使用的内存越来越多,在GC之后内存使用量下降。

因此,我想知道您的应用程序是否真的会因为OOM而崩溃。您可以在测试环境中尝试一下,并获取OOM JVM转储文件进行分析,这将更有用。

顺便说一句,我建议使用VisualVM而不是MAT,因为MAT将包括一些无法访问对象作为GC根。这将使内存分析非常低效,并给出与其他工具不同的结果,我在我们的一个项目中遇到过这种情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接