在Java垃圾回收日志中,“GC--”是什么意思?

11

我们已经打开了详细的GC日志记录以追踪一个已知的内存泄漏,并在日志中得到了以下条目:

...
3607872.687: [GC 471630K->390767K(462208K), 0.0325540 secs]
3607873.213: [GC-- 458095K->462181K(462208K), 0.2757790 secs]
3607873.488: [Full GC 462181K->382186K(462208K), 1.5346420 secs]
...

我理解前两个,但是"GC--"是什么意思?


2
根据时间戳和内存量,我猜测它执行了垃圾回收,但失去了可用内存(因为其他对象在同时被创建)。 - Yishai
Yishai,谢谢,这很有道理。不确定是否正确,但很可能是正确的。 - RodeoClown
想把它放在答案里,这样我就可以投票/接受它了吗? :) - RodeoClown
你使用的JDK版本是什么?请提供完整的版本号。 - Trenton
5个回答

13

我在GC输出中看到了这种类型的行:

44871.602: [GC-- [PSYoungGen: 342848K->342848K(345600K)] 961401K->1041877K(1044672K), 0.1018780 secs] [Times: user=0.16 sys=0.00, real=0.11 secs]

我已经阅读了Yishai的答案,这很有道理,但我想亲自查看Java GC源代码,在JVM打印GC日志中为什么会出现“--”。

因为据我所知,Young Gen的“Parallel Scavenge”是一种停止全球的GC,所以不可能有任何对象在此GC期间并行创建。(请参见https://blogs.oracle.com/jonthecollector/entry/our_collectors

您可以在jdk源代码中找到这个(请参见http://hg.openjdk.java.net/jdk7/jdk7) g1CollectedHeap.cpp和psScavenge.cpp

jdk7-ee67ee3bd597/hotspot/src/share$ egrep -h -A2 -B5 -r '"\-\-"' *
# G1 Collector
if (evacuation_failed()) {
  remove_self_forwarding_pointers();
  if (PrintGCDetails) {
    gclog_or_tty->print(" (to-space overflow)");
  } else if (PrintGC) {
    gclog_or_tty->print("--");
  }
}
--
# Parallel Scavenge Collector
promotion_failure_occurred = promotion_failed();
if (promotion_failure_occurred) {
  clean_up_failed_promotion();
  if (PrintGC) {
    gclog_or_tty->print("--");
  }
}

使用Parallel Scavenge收集器的GC原因

Young GC遇到了提升失败的情况(参见http://mail.openjdk.java.net/pipermail/hotspot-gc-use/2010-March/000567.html):

提升失败是指一次扫描未能成功,因为老年代中没有足够的空间来完成所有所需的提升。本质上,这次扫描被撤销,然后对整个堆进行完整的STW压缩。

'没有足够的空间'并不一定意味着老年代空间不足,而是老年代过于分散(参见http://blog.ragozin.info/2011/10/java-cg-hotspots-cms-and-heap.html):

[...] 即使有足够的可用字节总数,也无法找到足够连续的内存来提升某个特定的大对象。

下面两个JVM选项可以帮助您分析堆内存的分散情况(参见http://blog.ragozin.info/2011/10/java-cg-hotspots-cms-and-heap.html):

-XX:+PrintPromotionFailure
-XX:PrintFLSStatistics=1

GC原因-使用G1收集器

G1中的疏散失败是指幸存者区域没有足够的空间来存储来自年轻代的幸存对象。

我不知道G1收集器是否会对疏散失败做出Full GC响应。


嗯,看起来非常明确 :) - RodeoClown

1

实际上,在我们自己的日志中遇到这个问题后,我和一位同事有一个替代解释,似乎更符合事实。

您会注意到在这个例子中,一个 Full GC 跟随着这个奇怪的 minor GC 行。我可以确认,当它在我们的日志中出现时,这总是发生的。您还可以看到 Young Gen 的开始和结束大小相等,我可以再次确认这总是这种情况。

我们认为这里发生的是 VM 开始了一个 Minor GC,然后在无法释放任何东西或花费太长时间无法释放任何东西后,决定进行 Full GC。


1

我从这里得到了以下信息:

前两行表示您进行了两次小型垃圾收集和一次大型垃圾收集。箭头前后的数字分别表示垃圾收集前后存活对象的总大小。在小型垃圾收集之后,计数包括不一定是活动的对象,但不能被回收的对象,因为它们直接活着,或者因为它们在年老代中或从年老代引用。括号中的数字表示可用空间的总量,不包括永久代中的空间,即总堆大小减去一个。第三行中的主要收集格式类似。标志-XX:+PrintGCDetails打印有关收集的其他信息。使用此标志打印的其他信息可能会随着虚拟机的每个版本而更改。特别是使用-XX:+PrintGCDetails标志的附加输出会随着Java虚拟机开发需求的变化而变化。幸存者空间的大小。小型收集大约需要四分之一秒。


0

Yishai 在评论中说:

考虑到时间戳和内存量,我猜测它执行了垃圾回收,但失去了可用内存(因为其他对象在并行创建)


0

嗨e5,没有特殊的垃圾收集器。普通的Sun VM。我只偶尔看到它,所以不是在每次Full GC之前(但我不能说每次它发生时是否直接在Full GC之前)。没有System.gc()。 - RodeoClown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接