无法创建新的本机线程错误 - 但很少有线程正在使用
我们有一个广泛部署的应用程序(运行它的数百个工作站)。在一个站点(并且只有一个站点 - 我们的产品被广泛部署到许多环境中),我们随机收到以下错误:
java.lang.OutOfMemoryError: 无法在 java.lang.Thread.start0(Native Method) at java.lang.Thread.start(未知来源)创建新的原生线程
操作系统是Windows 7 64位我们在32位JVM中运行( 1.7.0_45)
使用Windows任务管理器,我可以看到该进程有39个本机线程(不是很多),因此我们的应用程序中没有线程泄漏...没有其他进程消耗大量线程(资源管理器有35个,jvisualvm有24个,iexplore有20个,...我没有确切的计数,但我们可能会查看用户总数的300个线程)。
我试图附加JVisualVM,但它无法连接到进程(可能是线程耗尽的b / c)。但是从我从JVisualVM获得的指标来看,Java线程的数量大约是22个实时和11个守护进程。
堆表现良好 - 堆为 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。一旦发生这种情况,一切都会分崩离析。这现在是非常可重复的(将相同的流运行到放气器中会导致内存峰值)
那么,我们是否可能正在研究JRE的zip库的问题?认为这样似乎很疯狂,但我真的很茫然。
如果我采用完全相同的流并在不同的系统上运行它(即使运行相同的JRE - 32位,Java 7u45),我们也不会遇到问题。我已经完全卸载了JRE并重新安装了它,没有任何行为变化。