在Java中滚动垃圾回收器日志

44

在Sun JVM中是否可以对垃圾回收日志进行滚动记录?

目前我使用以下方法生成日志:

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -verbose:gc -Xloggc:gc.log 

但是我必须使用FIFO队列和rotatelogs手动旋转它们,以创建每天的新日志。我希望有更好的解决方案。

也许有一种方法可以从Java内部访问这些日志条目,因此我可以将它们重定向到log4j?

编辑:FIFO队列的解决方案不够好,因为如果从该队列中读取的进程(例如rotatelogs)读取速度过慢,它将减缓整个JVM的速度(显然Sun / Oracle同步进行GC日志记录)。


你的解决方案听起来很不错,你对它有什么不喜欢的地方吗?你有一个共存:旋转发生在你的Java调用附近(配置日志记录),而不是在应用程序代码中(应该对日志记录毫不知情)。 - Mark Peters
我的直觉会说不行,这不可能或者如果可能的话,只能通过一个私有、受限的API实现,但你可能不想强制将其添加到你的应用程序中。 - Mark Peters
-XX:+PrintGCDateStamps不适用于Java5吗? - rafa.ferreira
6个回答

89
HotSpot JVM中已经添加了对GC日志轮转的内置支持。 它在RFE 6941923中有详细描述,并且可以在以下版本中使用: 有三个新的JVM标志可以用来启用和配置它:
  • -XX:+UseGCLogFileRotation
    必须与-Xloggc:<filename>一起使用;
  • -XX:NumberOfGCLogFiles=<number of files>
    必须大于等于1,默认为1;
  • -XX:GCLogFileSize=<number>M (or K)
    默认设置为512K。

5
-XX:NumberOfGClogFiles 应该使用大写字母 L (-XX:NumberOfGCLogFiles) - Szymon Jednac
2
很遗憾这不是按天记录的,我认为大小滚动没有意义,通常我只想查看给定日期的日志,而不是 123MB 的日志文件 :) - Krzysztof Krasoń
4
大小滚动的重点在于限制日志文件的总大小(为NumberOfGCLogFiles * GCLogFileSize),这样可以避免设备空间不足的错误,这比查找特定日期的日志带来的不便更为重要。 - vboerchers
@krzyk 一个解决方法可能是记录到一个带有日期时间戳的命名文件中,并每日重新启动记录机制。 - Thorbjørn Ravn Andersen
@ThorbjørnRavnAndersen 如何在不重启JVM的情况下“每天重新启动日志记录机制”? - hidralisk

7
如果您不能升级Java版本以使用旋转GC日志的新标志,那么您可以在每次应用程序启动时指定不同的GC文件:
JAVA_OPTS="-Xms1024m -Xmx1024m -XX:MaxPermSize=256m -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/path/to/log/dir/gc.log-"`date +%Y-%m-%d-%H-%M`

当引用setenv时,通常在启动或关闭时,它将引用一个不同的日志文件。在Unix中,这可以用作“轮换”日志的方法。

注:setenv是Unix系统中的一个命令,用于设置环境变量。

更好的方法是将详细输出附加到一个文件中,并使用logrotate或类似工具轮换此文件。这样可以获得更长的日志,而不会分散在不同的文件中。 - Stian Lund
2
@Pathduck 这个答案适用于旧版本的Java。这意味着您无法将内容附加到Xlogcc中。我的实验还表明,您无法使正在运行的Java关闭并重新打开日志。您也无法使其寻找(EOF),Java会记住Xloggc内的文件位置。非常麻烦。 - kubanczyk
@Pathduck 不幸的是,并不是所有系统都具有logrotate。这个解决方案是为那些必须在没有logrotate的情况下操作的管理员发布的。以这种方式添加日期也非常适用于旋转catalina.out :-) - Underverse
将日期附加到名称的问题是,经过六个月和一堆服务器重新启动后,您会得到很多杂乱无章的日志,并且除非您已经设置logrotate来清理这些日志,否则它们将保留在那里。对于一个或两个服务器来说不是问题,但对于数百个服务器来说可能成为一个问题,或者不是问题,这取决于您对杂乱无章的日志目录有多么强迫症 :)另请参见:https://dev59.com/Bmsy5IYBdhLWcg3wvghl#10499244 - Stian Lund
1
我同意。因此: find /opt/app/data/gc -type f -mtime +69 -exec rm -rf {} \; - Underverse
显示剩余2条评论

4

您尝试过这些新选项吗?

我像这样尝试了jdk7u7、jdk7u6和jdk6u35:

java -Xloggc:gc.log -XX:+PrintGCDetails -XX:+UseGCLogRotation -XX:NumberOfGClogFiles=3 -XX:GCLogFileSize=10M

但是每个版本都会出现这个错误:
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

此处提到了7u2的Bugfix #6941923: http://www.oracle.com/technetwork/java/javase/2col/7u2bugfixes-1394661.html


1
你的标志错误了 - 应该是“-XX:+UseGCLogFileRotation”(你在标志中漏掉了“File”)。 - Ryan
1
而“-XX:NumberOfGClogFiles”中缺少大写字母“L”(因为它最初在我的答案中)。 - Johan Kaving
这不是Blazej的错,错别字在他引用的错误文档中(http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6941923)。 - t0r0X

1
一个有趣的方法是将gc.log重定向到命名管道 -Xloggc:/my/named/pipe 如何将GC日志写入命名管道 然后从应用程序本身读取该管道: 如何从Java中打开Windows命名管道 并从代码记录到任意(例如异步滚动)logback记录器。
在Windows机器上尝试过。不幸的是,在Windows上设置比在Linux上更棘手。
在Windows上,它基本上需要使用额外的Powershell脚本(也可以是专用应用程序)来帮助。 这个示例项目还包含一个演示应用程序,可以立即用于测试通过SLF4J将GC日志重定向到Logback。

据我理解,您必须有一个消费者监听命名管道,否则它最终会阻塞。 - Thorbjørn Ravn Andersen
是的,那就是脚本项目所包含的内容。它还包含了一个“阀门”,以防万一没有接收到消息,还有几条描述的注释。 - Sergey Shcherbakov
该组件是共存的,并且与主JVM一起运行,可以编写脚本以一起启动。您的代码还依赖于它所在的JVM进程以及某些操作系统进程,通常还依赖于其他应用程序服务等。但是,是的,这些示例更多是实验性质,而不是准备用于生产。主要是因为我没有花时间进行负载测试、调整性能、线程模型,也不知道PowerShell有多稳定。在Linux中,应该有更好的方法来实现这样的日志输出循环。 - Sergey Shcherbakov
问题不在于共存和沿着JVM运行,而是如果出现任何故障,应用程序就会无限期地挂起。 - Thorbjørn Ravn Andersen
1
当你真正获得这方面的经验时,听到它们会非常有趣(可能值得写一篇或两篇博客文章)。 - Thorbjørn Ravn Andersen
显示剩余4条评论

0

使用-XX:+UseGCLogFileRotation会导致Solaris和JDK版本1.7.0_80 - 1.7.0_97和1.8.0_20 - 1.8.0_77存在一些严重的长时间安全点问题。


0

我最终通过在我的应用程序中生成一个新线程并定期发送jcmd日志轮换命令(基于cron表达式)来解决了这个问题。

这是一种非传统的方法,因为您将使用Oracle的Attach API,尽管这种方法适用于我们每小时旋转GC日志的用例。


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