我有一个在 JBoss 上运行的 J2EE 项目,最大堆大小为 2048m,这在负载测试下给出了奇怪的结果。我已经对堆和 CPU 使用情况进行了基准测试,并收到了以下结果(系列 1 是堆使用情况,系列 2 是 CPU 使用情况):
似乎堆在 A 周围得到了正确的使用和垃圾收集。然而,当它到达 B 时,似乎存在某种瓶颈,因为有可用的堆空间,但它永远不会打破想象线。同时,在 C 处,cpu 使用率急剧下降。在此期间,我们还会收到“OutOfMemoryError(超出 GC 开销限制)”,这对我来说意义不大,因为有可用的堆空间。
我的猜测是存在某种瓶颈,但具体是什么我什至无法想象。你会如何建议去寻找问题的原因?我分析了内存使用情况并注意到一个类有很多实例(大约一百万),但这些实例的总大小相当小(如果我没记错的话大约 50MB)。
编辑:服务器专用于此应用程序,给出的 CPU 使用率仅用于 JVM(JVM 之外不应该有任何显着的 CPU 使用率)。内存使用仅针对堆,不包括 permgen 空间。此问题是可重现的。我主要担心的是围绕 B 遇到的限制,对此我还没有找到合理的解释。
结论:原来这是由同时调用了一堆长时间运行的 SQL 查询引起的。返回的结果集也非常大,可能解释了 OOME。我仍然没有合理的解释为什么 B 似乎有一些限制。
最佳答案
从错误消息看来,JVM 正在使用并行清道夫算法进行垃圾回收。当 a lot of time is spent on GC, but not a lot of the heap is recovered 时,消息与 OOME 错误一起被转储.
Sun的文档没有具体说明98%的总消耗时间是读作进程CPU利用率的98%还是CPU本身的利用率。无论哪种情况,我都必须得出以下推论(信息有限):
- 垃圾收集器或 JVM 进程没有足够的 CPU 使用率,很可能是由于其他进程同时消耗 CPU。
- 垃圾收集器没有足够的 CPU 使用率,因为它是一个低优先级线程,而 JVM 中的另一个内存密集型(但不是 CPU 密集型)线程正在同时工作,这导致无法 de-分配内存。
基于上述推论(所有推论、其中一个推论可能为真或都不为真),就用户而言,将您获得的图表与应用程序的运行时行为相关联是值得的。换句话说,您可能会发现确定是否启动了其他进程(当您的问题发生时)或正在运行的应用程序部分(同样,当问题发生时)很有用。
无论如何,上面引用的页面确实提供了一个选项来禁用 GC 算法使用的 GC 开销限制。
编辑:如果问题周期性发生,并且可以重现,则可能是内存泄漏,否则(即偶尔发生),您最好调整 GC 算法或甚至改变它。
关于performance - Java 堆瓶颈 - 如何确定原因?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3334232/