我在两台不同的服务器(oracle jdk7)上有两个不同的 tomcat 7 实例,硬件配置几乎相同(均 > 24 GB RAM)。 两个 tomcat 服务器具有相同的配置,并且在这些服务器上部署了相同的 Web 应用程序。 catalina opts 如下:
-XX:PermSize=128m -XX:MaxPermSize=512M -Xmx2048m -XX:+CMSIncrementalMode -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC
在运行负载测试(强调使用大量并行执行请求的 REST API)时,其中一台服务器抛出错误java.lang.OutOfMemoryError: Java heap space
(这里是堆栈跟踪:http://pastebin.com/wuS1MVCC),另一台服务器运行正常。我不知道为什么会发生这种情况。有人遇到过类似的问题吗?
答案1
所以发生的情况是,您的一个 Tomcat JVM 正试图超出您为其分配的 2048 MB 堆。
听起来您正在寻找一个具体的答案,就像寻找一份要尝试的事情的清单一样,因此您可以这样做:
堆耗尽要么是由于内存泄漏,即每次请求时都会有一点内存泄漏,要么是在负载情况下,可能是因为您向 JVM 发送了超出其处理能力的负载。您需要确定其中哪一个是问题,因此首先查看如何生成负载。
如果问题只发生在高并行请求水平,而从未发生在低并行请求水平,则问题在于请求数乘以处理每个请求所需的内存太大。您要么需要让每个请求使用更少的内存,要么以某种方式限制并发性。
如果问题发生在处理了一定数量的请求之后(无论并发性如何),则说明存在内存泄漏。您需要找到它并回收内存。
在任何一种情况下,拥有一个好的堆内存分析器都会对你大有帮助。有很好的商业分析器,如 YourKit Java Profiler,也有免费的分析器,如 Eclipse Memory Analyzer。找到一个适合你的工具,并学习如何使用它来查看哪些东西占用了内存。请注意,你不一定需要使用该工具来启动你的程序——如果你正在服务器上运行负载测试,那么你可以使用 JDK 中的 jmap 命令行工具在文件中捕获堆转储,然后使用你的工具分析转储文件。该工具将向你显示哪些对象占用了堆中的空间。
答案2
有某物两个系统之间有差异,否则行为不会有差异。请注意,在 GC 上花费太多时间而结果太少也会导致错误。运行接近堆的限制会使 GC 出现此类问题。
GC 时间过长和 OutOfMemoryError
如果在垃圾收集上花费了太多时间,并发收集器将抛出 OutOfMemoryError:如果在垃圾收集上花费了超过总时间的 98%,而回收的堆空间不足 2%,则会抛出 OutOfMemoryError。此功能旨在防止应用程序因堆空间太小而长时间运行却进展不大或毫无进展。如有必要,可以通过在命令行中添加选项 -XX:-UseGCOverheadLimit 来禁用此功能。