开发人员可利用 JVM 参数“-XX:+UseGCLogFileRotation”来旋转 GC 日志文件。 示例:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc.log
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M
从上面的例子可以看出,JVM 会在 GC 日志达到 20MB 时对其进行旋转。其最多会生成 5 个文件,后缀名分别为 gc.log.0、gc.log.1、gc.log.2、gc.log.3、gc.log.4。
但此方式也有一些弊端:
# a.丢失旧的 GC 日志
假设您配置了 -XX:NumberOfGCLogFiles=5,那么在一段时间内将创建 5 个 GC 文件:
gc.log.0 ← 最老的 GC 日志内容
gc.log.1
gc.log.2
gc.log.3
gc.log.4 ← 最新的 GC 日志内容
最近的 GC 日志内容将写入“gc.log.4”中,最早的 GC 日志内容将保留在“gc.log.0”内。
在程序生成比“-XX:NumberOfGCLogFiles”配置值更多的 GC 日志时,保留在 gc.log.0 中最早的 GC 日志内容将被删除。gc.log.0 中将写入新的 GC 事件。这就意味着您可能无法获得所有已生成的 GC 日志。也就无法完整地看到所有事件。
# b.GC 日志混乱
假设程序创建了 5 个 GC 文件,比如:
gc.log.0
gc.log.1
gc.log.2
gc.log.3
gc.log.4
然后您重启了程序。那么新的 GC 日志会被写入 gc.log.0 文件中,旧的 GC 日志内容将会留在 gc.log.1、gc.log.2、gc.log.3、gc.log.4 中。
gc.log.0 ← 重启后的 GC 日志文件内容
gc.log.1 ← 重启前的 GC 日志文件内容
gc.log.2 ← 重启前的 GC 日志文件内容
gc.log.3 ← 重启前的 GC 日志文件内容
gc.log.4 ← 重启前的 GC 日志文件内容
因此,新的 GC 日志内容会与旧内容混在一起。所以为了解决此问题,您可能必须在重启程序前将所有旧 GC 日志移动到其他文件夹中。
# c.将 GC 日志转发至中心位置
此方法中,写入 GC 日志的当前活动文件将带上“.current”扩展名。例如,如果 GC 事件当前写入的文件是“’gc.log.3”,那其将被命名为:“gc.log.3.current”。
如果您想将 GC 日志从各个服务器中转发至中心化位置,则可使用大多数 DevOps 工程师都会使用的“rsyslog”。不过这样的命名约定给使用“rsyslog”带来了不小的挑战,可参考这篇博客文章进一步了解相关内容。
# d.借助工具
现在,如果需要使用 GC 工具(如:gceasy.io、GCViewer 等)来分析 GC 日志,您就需要上传多个 GC 日志文件,而不是单一的 GC 日志文件。
建议解决方案
我们可以在 GC 日志文件的后缀名中加上 JVM 重新启动时的时间戳,这样 GC 日志文件的位置将变成唯一值。新的 GC 日志也就不会覆盖旧的 GC 日志了。这一点可通过在 GC 日志文件名称后加上后缀“%t”实现,如下所示:
"-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc-%t.log"
“%t”能在 GC 日志文件名中加入下列格式的时间戳:“YYYY-MM-DD_HH-MM-SS”。这样生成的 GC 日志文件名就会变成“gc-2019-01-29_20-41-47.log”这样的形式
这种简单的解决方案就能解决所有“XX:+UseGCLogFileRotation”的弊端
Leave a Reply