我们使用并行gc和1.7.0_71。
java -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -version
-XX:InitialHeapSize=258222272 -XX:MaxHeapSize=4131556352 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedOops -XX:+UseParallelGC
java version "1.7.0_71"
Java(TM) SE Runtime Environment (build 1.7.0_71-b14)
Java HotSpot(TM) 64-Bit Server VM (build 24.71-b01, mixed mode)我们看到每隔几分钟就会在我们的应用程序中创建大量垃圾。小gc每隔几秒钟触发一次,主gc每2分钟触发一次。通过分析,我们发现每个大小为173 K的180万个char数组对象留在堆中。小gc无法恢复它们。在进行堆转储时,我们在Eclipe的剩余部分找到了许多对象。MAT直方图显示了大量的char数组(它们被呈现为html),但是对于char数组的所有传入引用和GC根的合并路径都是不可访问的,但是只有完整的gc才能恢复它们,而不是小GC。当没有引用对象时,为什么小GC不能恢复它们?日食垫图片附呈。请看所有的参考资料是无法到达的。

有一些对请求对象的引用,通过设置空来清除它们。
修复之前,有对HttpServletRequest对象的挂起引用。

所有与此问题相关的图片:Pm7a?dl=0
总体堆

堆直方图视图

现在的问题是
发布于 2015-05-11 22:57:27
当没有引用对象时,为什么小GC不能恢复它们?
很可能是因为他们是老一代。小GCs只在年轻一代中处理不可到达的对象。
这种情况通常发生在物体寿命比年轻一代的寿命长的时候。例如,如果涉及缓存,将结果保存一段时间,或者请求生存期超过GC interval * tenuring threshold时。
-XX:+PrintTenuringDistribution可能被证明是有用的。
首先,您可以尝试通过-XX:MaxGCPauseMillis=...提供一个暂停时间目标。ParallelGC可能会遇到它。
如果这没有帮助,您可以重构您的代码,以减少对象的生存期,或降低分配率,以减少较小的GC的频率。
请注意,最重要的ParallelGC是吞吐量收集器,就CPU周期而言,它比并发收集器更高效,但它通常无法满足像这些目标那样的低暂停时间目标。
你认为搬到G1 GC会有帮助吗?
如果你关心的是暂停时间,很有可能。您可能还想尝试CMS。
如果您想尝试G1,您可能应该切换到java 8,它的启发式方法随着时间的推移有了很大的改进,而且它还在成熟。
吞吐量会否下降?
可能吧。这取决于是否有空闲的CPU容量,以及如何定义/度量吞吐量。即使在不太有利的情况下,这一下降幅度也可能不会很大。
G1 GC的最佳可用选项是什么。
G1应该进行自调优(超出用户提供的暂停和吞吐量目标,这些目标也适用于ParallelGC)。因此,只需启用它,并看看它是否提供了可接受的性能。
发布于 2015-05-13 00:36:41
类似的问题也发生在我们的网上。一个引用泄露到了Gen1中--并且老化到了第二代,最终导致内存积累成30Gb+大小。
虽然与JAVA没有直接关系,但是我们通过构建一个定制的内存管理器来解决类似的问题,该管理器可以子分配byte[]。这使得我们可以在没有加载GC的情况下存储数亿个在进程中的对象。该解决方案非常适合缓存,而且比进程外Redis/memcache快得多。它用超高速序列化器序列化对象,这样“引用”就变成了“指针”(我们的两个It结构),这样GC就不会被扫描1000000000个对象重载。
参见:7hukyejQ
https://stackoverflow.com/questions/30176934
复制相似问题