无法创建新的本机线程错误 - 但很少使用线程

Kev*_*Day 15 java memory multithreading

我们有一个广泛部署的应用程序(运行它的几百个工作站).在一个站点(并且只有一个站点 - 我们的产品被广泛部署到许多环境中),我们随机出现以下错误:

java.lang.OutOfMemoryError:无法在java.lang.Thread.start(未知来源)的java.lang.Thread.start0(本机方法)中创建新的本机线程

操作系统是Windows 7 64位我们运行在32位JVM(1.7.0_45)

使用Windows任务管理器,我可以看到该进程有39个本机线程(不是很多),所以我们的应用程序中没有线程泄漏...没有其他进程占用大量线程(Explorer有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进程的正常范围内.

这让我有两个可以想到的可能性:

  1. 一些瞬态条件导致创建大量线程(但在我们有机会进行诊断之前丢弃这些线程)
  2. 由于某些原因,内存分段正在扼杀我们 有趣的是,最大的可分配块(每个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 ....
Run Code Online (Sandbox Code Playgroud)

在一些非常小的流(我现在有4个例子)我们正在收缩,上面发生了.当它发生时,VMMap将进程堆(而不是JVM堆,但实际的本机堆)高达2GB.一旦发生这种情况,一切都会崩溃.这现在是非常可重复的(在deflater中运行相同的流导致内存尖峰)

那么,我们是否可能会看到JRE的zip库存在问题?看起来很疯狂,但我真的很茫然.

如果我采用完全相同的流并在不同的系统上运行它(甚至运行相同的JRE - 32位,Java 7u45),我们就不会遇到问题.我已经完全卸载了JRE并重新安装它而没有任何行为改变.

Kev*_*Day 4

终于明白了这一点。

我们处理了几个数据流(此站点的 1000 万个数据流中有 4 个)最终创建了大量的 DeflaterOutputStream 对象。我们使用的第三方库在流上调用 finish() 而不是 close()。底层的 Deflater 终结器正在清理一切,因此只要负载不太高,就不会有问题。但过了一个临界点,我们开始遇到这个问题:

http://jira.pentaho.com/browse/PRD-3211

这导致我们这样做:

https://bugs.java.com/bugdatabase/view_bug?bug_id=4797189

发生这种情况几个小时后,系统终于陷入了无法摆脱的困境,无法在我们需要时创建本机线程。

修复方法是让第 3 方库关闭 DeflaterOutputStream。

所以绝对是本机资源泄漏。如果其他人遇到过类似的问题,VMMap 工具对于最终追踪导致问题的数据流是必不可少的。