避免使用 -XX : +UseGCLogFileRotation
(给ImportNew加星标,提高Java技能)
编译:唐尤华
链接:dzone.com/articles/try-to-avoid-xxusegclogfilerotation
开发者通过 JVM 参数 `-XX:+UseGCLogFileRotation` 实现 GC 日志轮转。
像下面这样:
```shell
"-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M"
```
指定上述参数,当日志文件大小增加到 20MB,JVM 会进行 GC 日志轮转生成最多5个文件,扩展名分别为 `gc.log.0`、`gc.log.1`、`gc.log.2`、`gc.log.3` 和 `gc.log.4`。
这种做法会带来以下问题:
1. 丢失旧的 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 事件。
2. GC 日志混合
假设某个应用创建了5个 GC 日志文件:
gc.log.0
gc.log.1
gc.log.2
gc.log.3
gc.log.4
接着,如果重启这个应用,现在新 GC 日志会写入 `gc.log.0`。重启前的日志信息保存在 `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 新日志与旧日志混在一起。要解决这个问题,可以在重启应用前把所有旧日志移动到一个独立的文件夹中。
3. 将 GC 日志转发到中央存储
采用这种方法,当前写入的日志文件会标记 `.current` 扩展,例如当前写入 `gc.log.3`,会命名为 `gc.log.3.current`。
如果要把 GC 日志从单个服务器汇总到中央存储,大多数 DevOps 工程师会使用 `rsyslog`。然而,正如[这篇博客][1]讨论的那样,这种命名方式给 `rsyslog` 带来了巨大挑战。
[1]:http://www.planetcobalt.net/sdb/forward_gc_logs.shtml
4. 工具
现在,有许多像 [GCeasy][2]、GCViewer 这样的工具可以用来分析 GC 日志,支持上传多个日志文件。
[2]:https://gceasy.io/
5. 推荐的解决方案
当 JVM 重启后可以为 GC 日志加上时间戳作为后缀,这样 GC 日志文件路径能够保持唯一。这样,新日志就不会覆盖掉旧的 GC 日志。通过为 GC 日志文件指定 `%t` 后缀可以实现,像下面这样:
```shell
"-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc-%t.log"
```
`%t` 后缀格式生成的时间戳格式为 `YYYY-MM-DD_HH-MM-SS`。因此,生成的日志文件名看起来像这样 `gc-2019-01-29_20-41-47.log`。
这种简单的方法解决了 `-XX:+UseGCLogFileRotation` 参数的所有缺点。
推荐阅读
(点击标题可跳转阅读)
看完本文有收获?请转发分享给更多人
关注「ImportNew」,提升Java技能
喜欢就点一下「好看」呗~