java - 避免在 Java CMS GC 中升级失败

标签 java performance garbage-collection concurrent-mark-sweep

我有一个使用 CMS 垃圾收集的 Java 应用程序,它每天都会遭受几次“ParNew(提升失败)”完整 GC(请参见下面的示例)。我知道当垃圾收集无法在老一代中找到足够的(连续的)空间来将新一代对象提升到其中时,就会发生提升失败。在这一点上,它被迫进行昂贵的 stop-the-world full GC。我想避免此类事件。

我已经阅读了几篇建议可能的解决方案的文章,但我想在这里澄清/巩固它们:

  1. -Xmx:增加堆大小,例如。从 2G 到 4G——为老一代提供更多余量的简单解决方案——根据我的经验,似乎工作得相当好
  2. -XX:NewRatio:增加 NewRatio,例如。从 2 到 4,为了增加老一代/减少新一代——给老一代更多的空间——到目前为止,从我的实验来看似乎没有太大的影响,如果有的话
  3. -XX:PromotedPadding:增加为避免升级失败而提供的填充量——但是我找不到任何关于为该参数赋予什么值的建议——有谁知道该值的含义、默认值是什么,或者尝试什么值(value)观?
  4. -XX:CMSInitiatingOccupancyFraction -XX:+UseCMSInitiatingOccupancyOnly:使 CMS 周期更快开始以避免老年代空间不足——我还没有尝试过这个解决方案——尝试什么值是合理的?默认值是多少?
  5. 不要在堆上分配非常大的对象:一个非常大的对象可能很难提升,因为它在老年代需要大量连续的可用空间——这不适用于我的应用程序,就我而言我知道

如果相关,这里是我当前的 GC 选项和升级失败事件之前的日志示例。

-Xmx4g -XX:+UseConcMarkSweepGC -XX:NewRatio=1

2014-12-19T09:38:34.304+0100: [GC (Allocation Failure) [ParNew: 1887488K->209664K(1887488K), 0.0685828 secs] 3115998K->1551788K(3984640K), 0.0690028 secs] [Times: user=0.50 sys=0.02, real=0.07 secs] 
2014-12-19T09:38:35.962+0100: [GC (Allocation Failure) [ParNew: 1887488K->208840K(1887488K), 0.0827565 secs] 3229612K->1687030K(3984640K), 0.0831611 secs] [Times: user=0.39 sys=0.03, real=0.08 secs] 
2014-12-19T09:38:39.975+0100: [GC (Allocation Failure) [ParNew: 1886664K->114108K(1887488K), 0.0442130 secs] 3364854K->1592298K(3984640K), 0.0446680 secs] [Times: user=0.31 sys=0.00, real=0.05 secs] 
2014-12-19T09:38:44.818+0100: [GC (Allocation Failure) [ParNew: 1791932K->167245K(1887488K), 0.0588917 secs] 3270122K->1645435K(3984640K), 0.0593308 secs] [Times: user=0.57 sys=0.00, real=0.06 secs] 
2014-12-19T09:38:49.239+0100: [GC (Allocation Failure) [ParNew (promotion failed): 1845069K->1819715K(1887488K), 0.4417916 secs][CMS: 1499941K->647982K(2097152K), 2.4203021 secs] 3323259K->647982K(3984640K), [Metaspace: 137778K->137778K(1177600K)], 2.8626552 secs] [Times: user=3.46 sys=0.01, real=2.86 secs] 

最佳答案

虽然增加内存确实是最简单和最通用的解决方案,但在这种情况下,我们似乎遇到了一个需要特定解决方案的特定问题。在我的案例中查看 GC 日志,我会看到这样的日志:

GC (CMS Initial Mark) [1 CMS-initial-mark: 2905552K(3145728K)]

这表明在 CMS 开始时,老一代已满约 92%(使用了 3.1Gb 中的 2.9Gb)。所以 JVM 决定“占用率”应该在 90% 左右。这是从默认值开始的变化,我认为大约是 68%。

显然,我的应用程序的行为方式让 JVM 认为这是一件好事。但是随后应用程序突然需要旧代中的更多空间来提升新代中的对象,这似乎让 JVM 大吃一惊。

关于添加 Gcflags

-XX:CMSInitiatingOccupancyFraction=50 -XX:+UseCMSInitiatingOccupancyOnly

我们不再看到任何“推广失败”事件。这些标志分别将初始占用率设置为 50%,并告诉 JVM 不要更改此分数。因此,一旦老一代超过 50%,它就会启动 CMS。这避免了它等到入住率达到 90% 左右,此时“促销失败”的可能性要高得多。

关于java - 避免在 Java CMS GC 中升级失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27599934/

相关文章:

docker - 如何在不访问容器的情况下在Docker Registry中使用GC

python-2.7 - 无法删除 matplotlib.animation.FuncAnimation 对象

java - 将 bean 属性的子集公开为 REST 服务的 XML 响应

java - 使用 setData() 获取 SWT 小部件附加的所有键/值对

performance - Apache是​​否缓存静态文件的压缩版本?

r - 将事件开始和结束时间转换为 R dplyr/tidyr 中多个组的分箱数据

performance - Oracle 性能和内存

java - 深克隆数组对象时遇到问题

java - arduino 发送数据失败

c# - 为什么在这个非常简单的场景中我的 .net 析构函数没有被调用?