【问题标题】:Rolling garbage collector logs in javajava中的滚动垃圾收集器日志
【发布时间】:2011-04-18 20:05:03
【问题描述】:

是否可以在 Sun JVM 中滚动垃圾收集器日志?

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

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

但我必须使用 fifo 队列和 rotatelogs 手动轮换它们,以便每天创建一个新日志。我希望有更好的解决方案。

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

编辑:使用 fifo 队列的解决方案还不够好,因为如果从此队列中读取的进程(例如 rotatelogs)读取速度变慢,它将减慢整个 jvm(显然 Sun/Oracle 会同步 gc 日志记录)

【问题讨论】:

  • 您的解决方案对我来说听起来不错;你不喜欢它什么?您有托管:轮换发生在您的 Java 调用(配置日志记录)附近,而不是在应用程序代码中(应该忽略日志记录)。
  • 我的直觉也会说不,这是不可能的,或者如果可能的话,它会通过一个私有的、受限制的 API,你可能不想强行进入你的应用程序。
  • -XX:+PrintGCDateStamps 不适用于 java5?

标签: java logging garbage-collection


【解决方案1】:

HotSpot JVM 添加了对 GC 日志轮换的内置支持。 它在RFE 6941923 中进行了描述,并在以下位置提供:

可以使用三个新的 JVM 标志来启用和配置它:

  • -XX:+UseGCLogFileRotation
    必须与-Xloggc:<filename>一起使用;
  • -XX:NumberOfGCLogFiles=<number of files>
    必须>=1,默认为1;
  • -XX:GCLogFileSize=<number>M (or K)
    默认设置为 512K。

【讨论】:

  • -XX:NumberOfGClogFiles 应该有一个大写 L (-XX:NumberOfGCLogFiles)
  • 很遗憾,这不是每天,我看不到大小滚动的意义,我通常希望查看给定日期的日志,而不是 123MB :)
  • 大小滚动的要点是日志文件的总大小受到限制(到 NumberOfGCLogFiles * GCLogFileSize),这避免了 no-space-left-on-device 错误并超过了查找日志的不便某一天。
  • @krzyk 一种解决方法可能是记录到带有日期戳的命名文件,并每天重新启动记录机制。
  • @ThorbjørnRavnAndersen 如何在不重启 JVM 的情况下“每天重启日志机制”?
【解决方案2】:

如果您无法升级您的 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 中,这可以用作“旋转”日志的方法。

【讨论】:

  • 更好的方法是将详细输出附加到文件并使用 logrotate 或类似方法旋转该文件。您可以获得更长的日志,而不是分解成一堆不同的文件。
  • @Pathduck 此答案适用于较旧的 java 版本。这意味着您不能附加到 Xlogcc。我的实验还表明,您不能导致正在运行的 java 关闭并重新打开日志。你也不能让它寻找(EOF),java会记住Xloggc中的文件位置。主要的 PITA。
  • @Pathduck 不幸的是,并非所有系统都有 logrotate。这个解决方案是为不得不做的管理员发布的。以这种方式附加日期对于旋转 catalina.out 也很有用 :-)
  • 在名称上附加日期的问题是,在六个月后,一堆服务器重新启动后,您最终会得到很多杂乱的日志,除非您设置了 logrotate 来清理这些日志,否则它们只会呆在那里。一台或两台服务器不是问题,但是对于数百台服务器,这可能是一个问题,也可能不是,这取决于您对混乱的日志目录的强迫症:) 另请参阅:stackoverflow.com/questions/8353401/…
  • 我同意。因此:find /opt/app/data/gc -type f -mtime +69 -exec rm -rf {} \;
【解决方案3】:

您是否尝试过这个新选项?

我试过这样的 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 的错误修复 #6941923:http://www.oracle.com/technetwork/java/javase/2col/7u2bugfixes-1394661.html

【讨论】:

  • 你的标志是错误的 - 它是“-XX:+UseGCLogFileRotation”(你在标志中缺少“文件”)
  • “-XX:NumberOfGClogFiles”中缺少大写“L”(因为它最初在我的回答中)
  • 这不是 Blazej 的错,错别字在他引用的错误文档中 (bugs.java.com/bugdatabase/view_bug.do?bug_id=6941923)。
【解决方案4】:

一个有趣的方法是将 gc.log 重定向到命名管道 -Xloggc:/my/named/pipe How to write GC log to named pipe

然后从应用程序本身读取该管道: How to open a Windows named pipe from Java?

并从代码中记录到任意(例如异步滚动)logback 记录器。

在 Windows 机器上试过了。不幸的是,在 Windows 上设置比在 Linux 上更棘手。

在 Windows 上,它基本上可以在附加 Powershell script 的帮助下工作(也可以是专用应用程序)。 这个sample project 还包含一个演示应用程序,可立即用于测试通过 SLF4J 将 GC 日志重定向到 Logback。

【讨论】:

  • 据我了解,您必须有一个消费者在收听命名管道,否则它最终会阻塞。
  • 是的,这就是脚本项目。它还包含一个“阀门”,用于没有收到消息的情况以及几个描述该消息的 cmets。
  • 尽管这很有趣,但这意味着您的应用程序现在依赖于这个外部组件才能正常运行。这很可能意味着我们不能在生产中使用它:-/
  • 该组件位于同一位置并与主 JVM 一起运行,可以编写脚本以一起启动。您的代码还依赖于它在其中运行的 JVM 进程和一些 OS 进程,并且通常依赖于其他应用程序服务等。但是,是的,这些示例是实验性的,而不是准备用于生产。主要是因为没有花时间在负载测试、调优性能、线程模型上,不知道PowerShell有多稳定。在 Linux 中应该有更好的方法来实现这样的日志输出循环。
  • 当你真正获得这方面的经验时,听到它们会很有趣(可能值得一两篇博文)。
【解决方案5】:

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

这是一种非常规的方法,因为您将使用 Oracle 的 Attach API,尽管该方法适用于我们每小时轮换 GC 日志的用例。

【讨论】:

    【解决方案6】:

    使用 -XX:+UseGCLogFileRotation 会导致 solaris 和 JDK 版本 1.7.0_80 - 1.7.0_97 和 1.8.0_20 - 1.8.0_77 出现严重的长安全点问题

    【讨论】:

      猜你喜欢
      • 2011-08-01
      • 2010-10-28
      • 1970-01-01
      • 2014-02-19
      • 1970-01-01
      • 2011-11-07
      • 1970-01-01
      • 1970-01-01
      • 2017-12-29
      相关资源
      最近更新 更多