我的Clojure应用程序中有一个奇怪的问题。
我正在使用http-kit编写基于websocket的聊天应用程序。
使用React作为单页面应用程序呈现客户端,当他们导航到主页(登录后)时,他们首先要创建一个websocket来接收实时更新和任何聊天消息等内容。你可以在这里看到这个网站:www.csgoteamfinder.com
我遇到的问题是经过一段不确定的时间,重启后30分钟甚至48小时,运行聊天服务器的JVM突然开始消耗所有CPU。当我用NR(New Relic)检查它时,我可以看到垃圾收集器正在使用所有这些时间 - 在这个阶段我不知道它在做什么。
我已经拍了很多截图,你可以看到效果。
您可以看到许多峰值,这些峰值对应于因垃圾收集器而导致的CPU使用率大幅增加。为了释放CPU,我通常必须重新启动JVM,我一直依赖于在我的松弛帐户中从NR接收CPU警报,以确保我快速跳过这些....但我真的需要找到它的根源问题
我最初的想法是,当客户端在结束时关闭它时,我可能会持有套接字引用,但事实并非如此。我一直在定期查看套接字计数,它相当稳定。
任何从哪里开始的想法?
亲切的问候,杰森。
答案 0 :(得分:3)
很难想象会出现什么问题。但首先我要做的是在崩溃时进行堆转储。可以使用-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<path_to_your_heap_dump>
JVM args启用此功能。 作为一般做法,不要将堆大小增加到服务器计算机上可用的物理内存大小。在极少数情况下,JVM无法转储堆空间,因为进程注定失败;在这种情况下,你可以使用gcore(如果你在Linux上,不确定Windows)。
一旦你抓住堆转储,用mat分析它,我已经调试了这样的应用程序,这完美地解决了任何与内存相关的问题。 Mat允许您深入剖析堆转储,以便在不分配非常小的堆空间的情况下确保找到内存问题的原因。
答案 1 :(得分:2)
如果你的程序在垃圾收集中花费了大量的CPU时间,那意味着你的堆已经满了。通常这意味着两件事之一:
-Xmx
)。首先尝试前者。为您的程序分配大量内存(根据您正在查看的图表,在您的情况下为16GB或更多)。看看你是否还有相同的症状。
如果症状消失,那么你的程序只需要更多的记忆。否则,您有内存泄漏。在这种情况下,您需要进行一些内存分析。在JVM中,通常这样做的方法是使用jmap
生成堆转储,然后使用堆转储分析器(例如jhat或VisualVM)来查看它。
(公平披露:我是名为fasthat的jhat fork的创建者。)
答案 2 :(得分:2)
很可能你的任期空间正在填补触发完整的收藏。此时GC会在一段时间内使用所有CPUS。
要诊断出现这种情况的原因,您需要查看您的晋升率(从年轻一代到终身空间的数据量)
我会考虑增加年轻一代,以降低晋升率。您还可以查看使用CMS,因为它具有更短的暂停时间(尽管它使用更多CPU)
答案 3 :(得分:1)
要按顺序尝试:
您已触发全球GC。 GC时间的增长速度快于线性,具体取决于内存量,因此实际减少堆空间将更频繁地触发全局GC并使其更快。
您还可以尝试更改GC算法。我们有一个系统,全球GC从200s(每24小时发生1-2次)下降到12s。是的,系统完全静止了3分钟,没有用户不高兴:-)你可以尝试-XX:+ UseConcMarkSweepGC
http://www.fasterj.com/articles/oraclecollectors1.shtml
对于JVM和类似的人,你总会有这样的停顿;它更多的是关于你获得它的频率,以及全球GC的速度。您应该进行堆转储并获取每个类的不同对象的计数。最有可能的是,你会看到你有数以百万计的其中一个,不知何故,你在不断增长的缓存或会话或类似的东西中保持指向它们的指针。
http://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks001.html#CIHCAEIH
您还可以开始使用至少包含2个节点的高可用性解决方案,这样当一个节点忙于执行GC时,另一个节点将不得不在一段时间内处理总负载。希望您不会同时在两个系统上获得全局GC。
像byte []和类似的大二进制对象是一个真正的问题。你有那些吗?
在某些时候,这些需要由全球GC压缩,这是一个缓慢的操作。许多基于数据处理JVM的解决方案实际上避免将所有数据作为普通POJO存储在堆上,并自行实现堆栈以克服此问题。
另一种解决方案是从JVM切换到Erlang。 Erlang几乎是实时的,他们没有整个堆的全局GC的概念。 Erlang有很多小堆。你可以在
上阅读一些相关内容Erlang比JVM慢,因为它复制数据,但性能更加可预测。两者都很难。我有一个基于Webocket的基于Erlang的解决方案,它确实运行良好。
因此,您遇到了JVM,Microsoft CLR等类似的预期和正常问题。在堆大小增加的情况下,它将在接下来的几年中变得更糟,更常见。