Java堆被无法访问的对象所淹没

我们已经开始在Java EE应用程序中遇到一些严重问题。 具体来说,应用程序在启动后几分钟内运行高达99%的旧代堆。 没有抛出OOM,但JVM实际上没有响应。 jstat显示老一代的大小根本没有减少,没有垃圾收集,kill -3说:

Heap PSYoungGen total 682688K, used 506415K [0xc1840000, 0xf3840000, 0xf3840000) eden space 546176K, 92% used [0xc1840000,0xe06cd020,0xe2da0000) from space 136512K, 0% used [0xe2da0000,0xe2da0000,0xeb2f0000) to space 136512K, 0% used [0xeb2f0000,0xeb2f0000,0xf3840000) PSOldGen total 1536000K, used 1535999K [0x63c40000, 0xc1840000, 0xc1840000) object space 1536000K, 99% used [0x63c40000,0xc183fff8,0xc1840000) 

VM选项包括:

 -Xmx2300m -Xms2300m -XX:NewSize=800m -XX:MaxNewSize=800m -XX:SurvivorRatio=4 -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+UseParallelGC -XX:ParallelGCThreads=4 

(我改为拥有2300m堆/ 1800m新发,试图解决问题)

一旦JVM达到“内存不足”状态(永远占用)并在其上运行Eclipse Memory Analyzer,我就接受了JVM的堆转储。

结果很有趣。 大约200Mb被各种对象占用(有些对象比其他对象多),但其余的,1.9Gb都无法访问(可能值得注意的是,多数被GSON对象占用,但我认为不是任何事物的指示,只说我们在服务器操作期间通过很多GSON对象流失)。

有关为什么VM有这么多无法访问的对象的任何解释,并且根本无法收集它们?

JVM:

 $ /0/bin/java -version java version "1.6.0_37" Java(TM) SE Runtime Environment (build 1.6.0_37-b06) Java HotSpot(TM) Server VM (build 20.12-b01, mixed mode) 

当系统到达这个停顿时,这就是详细的GC继续打印出来的:

 922.485: [GC [1 CMS-initial-mark: 511999K(512000K)] 1952308K(2048000K), 3.9069700 secs] [Times: user=3.91 sys=0.00, real=3.91 secs] 926.392: [CMS-concurrent-mark-start] 927.401: [Full GC 927.401: [CMS927.779: [CMS-concurrent-mark: 1.215/1.386 secs] [Times: user=5.84 sys=0.13, real=1.38 secs] (concurrent mode failure): 511999K->511999K(512000K), 9.4827600 secs] 2047999K->1957315K(2048000K), [CMS Perm : 115315K->115301K(262144K)], 9.4829860 secs] [Times: user=9.78 sys=0.01, real=9.49 secs] 937.746: [Full GC 937.746: [CMS: 512000K->511999K(512000K), 8.8891390 secs] 2047999K->1962252K(2048000K), [CMS Perm : 115302K->115302K(262144K)], 8.8893810 secs] [Times: user=8.89 sys=0.01, real=8.89 secs] 

解决了

正如Paul Bellora所说,这是由于JVM内部创建的对象数量太大而导致的。 此时调试变得非常繁琐。 我最终做的是,使用自定义JVM代理检测类。 检测将计算方法和构造函数调用。 然后检查计数。 我发现一个不起眼的单一操作会产生大约200万个对象,并触发某些单独的方法大约150万次(不,没有循环)。 通过与其他人比较缓慢来确定操作本身。 您也可以使用任何热点分析器(类似于visualVM),但是我遇到了各种各样的麻烦,所以最终写了我自己的。

我仍然认为JVM的行为是一个谜。 它看起来像垃圾收集器来到一个停顿,并不会清理任何更多的内存,但内存分配器期望它(因此没有抛出OOM)。 相反,我本以为它能够清除所有无法访问的内存。 但是应用程序行为不会好一些,因为大部分时间都会花费在垃圾收集上。

有关为什么VM有这么多无法访问的对象的任何解释,并且根本无法收集它们?

(根据我们在评论中的交流),听起来这不是传统的内存泄漏,而是一些不断向新对象发送垃圾邮件的逻辑,以至于GC难以跟上当前的架构。

罪魁祸首可能是例如某些API请求正在进行多次,或者被“卡住”在一些错误的状态,如我所描述的无限分页场景。 无论哪种情况归结为数百万个响应gson对象(指向String s(指向char[] s))被实例化然后才有资格获得GC。

正如我所说,你应该尝试隔离问题请求,然后调试并进行测量,以确定这是应用程序或其中一个库的错误或可伸缩性问题。

根据您列出的统计数据,我发现很难相信您有1.9G无法访问的数据。 它看起来更像是达到GC Overhead Limit

考虑

937.746:[Full GC 937.746:[CMS:512000K-> 511999K(512000K),8.8891390 secs] 2047999K-> 1962252K(2048000K),[CMS Perm:115302K-> 115302K(262144K)],8.8893810 secs] [次:用户= 8.89 sys = 0.01,real = 8.89秒]

如果是这样,那么Full GC会释放85K的数据。 如果你有1.9G无法访问的代码,你会看到2047999 -> ~300000

 object space 1536000K, 99% 

意味着以某种方式创建和存储的东西它逃脱了一个方法,现在可能永远存在。

我需要看到更多的证据表明你有1.9G的无法访问的数据,然后只是被告知。