【问题标题】:Everyday GC is running on same time每天 GC 在同一时间运行
【发布时间】:2011-09-30 02:39:33
【问题描述】:

在我的服务器中,每天凌晨 3:00 GC 正在运行,Heapspace 正在快速填充。

这会导致网站中断。任何输入?

以下是我的 JVM 设置。我正在使用 JBOSS 服务器。

-Dprogram.name=run.sh -server -Xms1524m -Xmx1524m -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -XX:NewSize=512m -XX: MaxNewSize=512m -Djava.net.preferIPv4Stack=true -XX:MaxPermSize=512m -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -Djavax.net.ssl.trustStorePassword=changeit -Dcom.sun.management.jmxremote.port=8888 - Djava.rmi.server.hostname=192.168.100.140 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote

任何建议真的很有帮助..

【问题讨论】:

  • 你有什么安排在凌晨 3 点运行吗?
  • 不..我们监控了日志并且没有其他调度程序正在运行..即使我测试了我在凌晨 2:30 重新启动服务器以便清除堆。直到凌晨 3:00,堆最多400MB。凌晨 3:00 突然 GC 开始运行,所有空间瞬间被填满。
  • 下面是 GC 失败的 gc.log 输出。任何输入都非常有用。 34277.999:34284.652:[CMS-concurrent-preclean-start] 34284.758:[GC 34284.758:[ParNew(升级失败):523392K->523392K(523840K),0.8095230 秒]34285.568:[CMS34286.1588888 : 0.662/1.506 secs] (并发模式失败): 998786K->984155K(1036288K), 6.4504900 secs] 1517815K->984155K(1560128K), 7.2603450 secs]

标签: jboss garbage-collection


【解决方案1】:

(结果有点长;最后有一个实际的修复建议。)

非常非常简单,当你使用 -XX:+UseConcMarkSweepGC 时的垃圾收集工作是这样的:

所有对象都在所谓的年轻代中分配。这通常是几百兆到一个 gig 的大小,具体取决于 om VM 设置、CPU 数量和总堆大小。年轻代在 stop-the-world 暂停中被收集,然后是并行(多 CPU)压缩(移动对象)收集。年轻代的大小是为了使这个停顿相当大。

当对象存活下来(仍然可以访问)年轻一代时,它们会被提升为“old-gen”(老一代)。

老年代是 -XX:+UseConcMarkSweepGC 起作用的地方。在默认模式下(没有 -XX:+UseConcMarkSweepGC),当老年代变满时,整个堆会立即收集和压缩(四处移动,消除碎片)在一个停止世界的副本中。该暂停通常会比年轻代暂停更长,因为涉及到整个堆,这更大。

使用 CMS (-XX:+UseConcMarkSweepGC),压缩老年代的工作大部分是并发的(意思是在后台运行,应用程序暂停)。这项工作也不是压缩的;它的工作方式更像 malloc()/free() 并且您会受到碎片的影响。

CMS 的主要优点是,当一切正常时,您可以避免与堆大小成线性关系的长时间停顿,因为主要工作是并发锥体的(涉及一些停止世界的步骤,但它们通常应该很短)。

两个主要缺点是:

  • 您会受到碎片的影响,因为 old-gen 未压缩。
  • 如果您没有在 old-gen 填满之前完成并发收集周期,或者如果碎片阻止分配,则整个堆的最终完整收集与默认情况下一样集电极。即,仅使用一个 CPU。这意味着当/如果您确实完成了一次完整的垃圾回收,则暂停时间将更长比使用默认回收器时的暂停时间。

现在...您的日志。 “并发模式失败”旨在传达对于另一个需要将幸存对象提升到老年代的年轻一代 GC,并发标记/清除工作没有及时完成。 “升级失败”是指在从young-gen到old-gen的过程中,由于碎片,无法在old-gen中分配对象。

除非您在 JVM 中遇到真正的错误,否则堆使用量的突然增加几乎可以肯定是来自您的应用程序、JBoss 或某些作用于您的应用程序的外部实体。所以我真的无能为力。但是,可能会发生两件事:

  1. 活动高峰导致堆使用量增加过快,导致并发收集无法及时完成。
  2. Old-gen 过于分散,尤其是在 old-gen 快满时会导致问题。

我现在还应该指出,CMS 的默认行为是出于性能原因尝试尽可能长时间地推迟并发收集(但不会 长)。它发生的越晚,收集的效率就越高(就 CPU 使用而言)。然而,一个权衡是你增加了不能及时完成的​​风险(这又会触发一个完整的 GC 和长时间的暂停)。它也应该(我没有在这里进行经验测试,但这是有道理的)导致碎片化成为一个更大的问题;基本上,当一个对象被提升时,old-gen 越完整,对象的提升将恶化碎片问题的可能性就越大(这里太长了,无法详细介绍)。

在你的情况下,我会做两件事:

  1. 继续找出导致该活动的原因。我会说它不太可能是 GC/JVM 错误。
  2. 重新配置 JVM 以更早地触发并发收集周期,以避免堆变得如此满,以至于碎片成为一个特别大的问题,并给它更多的时间来及时完成,即使在你突然的活动高峰期。

您可以使用 JVM 选项最轻松地完成 (2)

  • -XX:CMSInitiatingOccupancyFraction=75
  • -XX:+UseCMSInitiatingOccupancyOnly

为了显式地强制 JVM 在特定级别的堆使用情况下启动 CMS 循环(在本例中为 75% - 您可能需要更改它;百分比越低,启动时间越早)。

请注意,根据您的应用程序中的实时大小(实际上是活跃且可访问的字节数),强制更早的 CMS 周期可能还需要您增加堆大小以避免 CMS 持续运行(不是充分利用 CPU)。

【讨论】:

  • Scode..非常感谢..它让我对GC有了一个完整的认识。我将对此进行测试并将更新发布给您。另一件事是有时正常 GC 运行时我们的系统线程正在迅速增加。有什么建议吗??
  • 正常是​​指频繁的年轻代收集吗?你是说你每次看到一个线程峰值,然后减少直到下一个?如果是这样,在我看来,这听起来像是在 GC 期间任何工作“排队”(例如,在传入的 tcp 套接字、accept() 积压等)导致的堆积。但这只是猜测,因为我对情况一无所知。
  • 我们还观察到,gc运行时CPU使用率突然上升到90,gc完成后又开始下降。这不是针对所有的young-generation collections,只针对一些collection。有没有我缺少配置吗?
  • 年轻代gc的描述不正确。年轻代 gc 是一个复制收集器。所有 mutator 线程都被停止,所有在 eden 和 from space 中的活动对象都被复制到 to-space 并且一些对象可能被提升到tenured空间。然后重新启动线程。请注意,复制收集器默认为压缩收集器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-23
  • 1970-01-01
  • 2021-06-26
  • 1970-01-01
  • 2017-02-25
  • 1970-01-01
相关资源
最近更新 更多