我们有一个广泛部署的应用程序(运行它的数百个工作站)。在一个站点(并且只有一个站点 - 我们的产品被广泛部署到许多环境中),我们随机收到以下错误:
java.lang.OutOfMemoryError: 无法在 java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Unknown Source) 创建新的本地线程
操作系统是 Windows 7 64 位 我们在 32 位 JVM ( 1.7.0_45 ) 中运行
使用 Windows 任务管理器,我可以看到该进程有 39 个本机线程(不是很多),所以我们的应用程序中没有线程泄漏......没有其他进程消耗大量线程(Explorer 有 35 个, jvisualvm 有 24 个,iexplore 有 20 个,......我没有确切的数量,但我们可能正在查看用户总数的 300 个线程)。
我试图附加 JVisualVM,但它无法连接到进程(可能是线程耗尽的 b/c)。但是从我可以从 JVisualVM 获得的指标来看,Java 线程的数量大约是 22 个 live 和 11 个 daemon。
堆表现良好 - 堆为 500MB,实际使用了 250MB。
该进程使用 -Xmx512m 启动
我们的进程显示内存使用量(在任务管理器中)为 597,744K。
工作站有 8GB RAM,其中仅使用了 3.8-4.0GB(我知道,32 位进程无法访问所有这些,但仍然有很多)
使用 VMMap,堆栈大小为 49,920KB,已提交 2,284K。
该进程显示 5358KB 空闲,空闲列表中最大的可分配块大小为 1,024K。
我使用了资源监视器,它显示提交 (KB) 为 630428,工作集 (KB) 为 676,996,可共享 (KB) 为 79,252,私有 (KB) 为 597,744
我完全不知道这里发生了什么。我已经阅读了大量关于此的文章,听起来在某些 Linux 系统上,每个用户的线程限制可能会导致问题(但这不是 Linux,其他文章中描述的问题通常都在谈论需要数千个线程 - 绝对不是我们这里的情况)。
如果我们的堆真的很大,我可以看到占用了线程可用的空间,但是 500MB 似乎是一个非常合理的小堆(尤其是对于具有 8GB RAM 的工作站)。
所以我已经用尽了我所知道的一切 - 有没有人对这里可能发生的事情有任何额外的指示?
编辑1:
我发现了这篇有趣的文章:Eclipse 因“无法创建新的本机线程”而崩溃——有什么想法吗?(里面是我的设置和信息)
他们暗示堆栈大小可能是问题所在。
这篇文章:在哪里可以找到 Sun/Oracle JVM 的默认 XSS 值?- 提供指向 Oracle 文档的链接,说明默认堆栈大小为 512KB。因此,如果我的应用程序有大约 40 个线程,我们正在查看 20 MB 的堆栈。500MB 堆。对于 32 位 Java 进程,这一切似乎都在正常范围内。
所以这给我留下了我能想到的两种可能性:
- 一些瞬态条件导致创建大量线程(但在我们有机会进行诊断之前这些线程被丢弃)
- 出于某种原因,内存分割正在扼杀我们。有趣的是,最大的可分配块(每个 VMMap 是 1MB)——这似乎不是很多……在另一台运行良好的机器上,最大的可分配块是 470MB……
那么,有没有关于如何检查内存分段的指针?
编辑2:
@mikhael ( http://blog.egilh.com/2006/06/2811aspx.html ) 链接的文章对 32 位 JVM 上允许的线程数进行了粗略计算。
我将假设:
操作系统进程空间限制:2GB 现代 JVM 需要 250MB(这是一个很大的假设 - 我只是将链接文章中的内容翻了一番)堆栈大小(默认 Oracle):512KB 堆:512MB PermGen:(不记得确切,但它是当然小于 100MB,所以让我们使用它)
所以我有一个最坏的情况:(2GB - .25GB - .5GB - .1GB)/.005GB = 230 个线程
编辑 3:
我最初应该包含的信息:在此问题发生之前,应用程序运行良好一段时间(如 24 到 48 小时)。该应用程序进行连续的后台处理,因此空闲时间很少。不知道这是否重要...
编辑4:
更多信息:从另一个故障中查看 VMMap,我看到本机堆耗尽。
堆大小为 1.2GB,仅提交了 59.8MB。
Java 运行时是这里的问题,还是本机资源没有正确释放的问题?就像可能没有释放的内存映射文件一样?
我们确实使用内存映射文件,所以我将重点放在这些文件上。
编辑4:
我认为我已经将问题归结为发生如下异常:
java.lang.OutOfMemoryError
at java.util.zip.Deflater.init(Native Method)
at java.util.zip.Deflater.<init>(Unknown Source)
at java.util.zip.Deflater.<init>(Unknown Source)
at java.util.zip.DeflaterOutputStream.<init>(Unknown Source)
at java.util.zip.DeflaterOutputStream.<init>(Unknown Source)
at ....
在一些非常小的流(我现在有 4 个示例)上,我们正在放气,上述情况发生了。当它发生时,VMMap 将进程的堆(不是 JVM 堆,而是实际的本机堆)增加到 2GB。一旦发生这种情况,一切都会崩溃。现在这是非常可重复的(将相同的流运行到 deflater 会导致内存激增)
那么,我们是否可能正在查看 JRE 的 zip 库的问题?认为会是这样似乎很疯狂,但我真的很茫然。
如果我采用完全相同的流并在不同的系统上运行它(即使运行相同的 JRE - 32 位,Java 7u45),我们就不会遇到问题。我已经完全卸载了 JRE 并重新安装了它,但行为没有任何改变。