详解Java的 统一JVM日志框架

4 分钟阅读

JAVA 9引入的统一JVM日志框架,旨在解决JVM内部各种各种混乱的打印日志和配置, 增强日志框架的功能(e.g. 滚动日志文件,异步日志.etc)并简化配置。JEP-158JEP-271

在1.8和以前的JVM参数调优中时不时会看到 -Xloggc:gc.log-XX:+PrintClassHistogram-XX:+PrintGCDetails-XX:+PrintHeapAtGC之类信息,这些信息到底是干什么的呢?到底该怎么迁移这些配置和参数呢?

统一JVM日志框架

本文使用OpenJDK 17 LTS。统一日志框架很明显能看出来很多现代化日志框架的影子,包括日志分级,按标签分类,滚动日志文件以及异步写等等,是JVM内部的一大重要变更和升级。以前项目组使用的Java 1.8,今年准备升级到Java 17,不可避免的这些优化和新特性都可以使用起来。

通过java -Xlog:help查看JVM统一日志框架的帮助文件。

转换后项目

日志等级

支持trace, debug, info, warning, error五个日志级别, 使用off禁用日志。

日志装饰器

根据设置的顺序装饰日志信息。设置为 none 不使用任何装饰器。

  描述
time(t),timemillis(tm),timenanos(tn) 当前时间和日期(ISO-8601 格式
uptime(u),uptimemillis(um),uptimenanos(un) 自JVM启动以来的时间,时间单位分别为秒,毫秒,纳秒
pid(p) 进程标识符
hostname(hn) 主机的hostname
tid(ti) 线程标识符
level(l) 与日志消息关联的级别
tags(tg) 与日志消息关联的标签

日志标签

统一日志框架应用于JVM内部的各个模块。 使用all开启全部。Github OpenJDK LogTag.hpp定义了169个标签

在OpenJDK中-XX:Print…相关的参数缺省值都是false, 并且推荐使用log进行配置。

模块 日志标签 描述
add redefine 重新定义方法时的日志。
age gc GC 任期分布及相关统计。
alloc gc GC 区域或空间分配。
annotation redefine 重新定义期间有关注释的日志,此标记似乎仅与redefine.
aot aot 提前机制日志。
arguments   JVM 选项警告或错误(只有错误,除非 level 是trace),例如,当使用应使用统一日志记录而不推荐使用的日志记录选项时。
attach   JVM 附件监听器
barrier gc,xxx GC 写屏障覆盖率
biasedlocking   偏向锁定机制src/hotspot/share/runtime/biasedLocking.hpp
blocks oopstorage,gc  
bot gc 块偏移表(带有偏移信息的侧数据结构,指示 GC 需要回溯多远才能找到对象的开头)。
breakpoint jvmti(重新定义) 断点设置或清除 JVMTI 的一部分,但记录在redefine?log 标记 root下。
bytecode jfr 有关由 执行的字节码修改的日志JFR,始终与jfr.
cds cds 类数据共享日志
census gc CMS 日志freelist信息。
class ,class,aot 类相关信息
classhisto gc  
cleanup safepoint 安全点清洁信息
codecache codecache,compilation 生成代码的代码缓存:已编译的 java 方法、运行时存根、转换帧等。隔离在 3 个堆中(非 nmethods,如缓冲区、适配器和运行时存根,已分析的 nmethods,未分析的 nmethods)。src/hotspot/share/code/codeCache.cpp
compaction gc G1GC 和 Parallel 的 GC 压缩
compilation compilation,jit 汇编
constantpool class,redefine, 常量池,主要在redefine
constraints class 类加载器约束
container os 容器 (CGroup) 相关 (cpu,cpuset,memory,mountinfo等等)
coops gc 压缩普通对象指针
cpu gc,os 记录时的 CPU 特性os,CPU 时间 igc
cset gc G1中的收藏集
data class 类加载器数据(分配空间或元空间)
datacreation perf 与 perf 数据创建相关
dcmd jfr JFR 诊断命令日志始终与日志标记根结合在一起jfr
decoder   与 Linux 上的 ELF 解码(可执行和可链接格式)相关。
defaultmethods   默认方法的运行时支持。类的一部分,但作为独立标签记录。
director gc ZGC相关
dump redefine 重新定义后重新定义有关旧方法或过时方法的调试日志记录。
ergo gc 人体工程学,通常与 结合gc,但可以单独找到(例如,关于线程本地握手的 2 个日志)
event jfr 关于 JFR 事件
exceptions exceptions,redefine 关于不同部分(解释器、安全点期间、编译器……)中的 Java 异常处理。与redefine.
exit gc GC 之后的 GC/heap 相关信息。LogStreamHandle(Debug, gc, heap, exit)
fingerprint class,aot 类指纹
free malloc 记录任何 C stdlib或调用,总是通过malloc标签记录。realloc free malloc, free
freelist gc 应该是空闲的内存块,用于 CMS 和元空间
gc gc GC 日志标签根。
handshake thread 线程本地握手src/hotspot/share/runtime/handshake.hpp
hashtables   独立时,它是内部分桶哈希表,是 a 的远亲HashMap,Hotspot 将它用于字符串和其他符号。CDS 使用另一个内部实现进行统计,因此该标签可以与cds?日志标签 root结合使用。
heap gc 各种GC堆相关的日志
humongous gc G1GC 中的巨大对象
ihop gc G1GC 中的堆占用(初始、当前、目标)
iklass class,redefine 内部类实例表示,日志标签根:redefine,classes
init gc,class log tag root主要是关于什么时候的类初始化class。关于结合gc.
inlining jit 方法内联信息
interpreter interpreter 解释日志,只记录带有oopmap和safepoint子标签的日志。
itables   JVM 开发过程中接口调用的 JVM 虚拟调用机制(需要非产品构建)。另见vtables标签。热点维基
jfr jfr JDK飞行记录器
jit jit inlining关于方法和的日志compilation
jni gc 仅在GC locker期间记录。
jvmti jvmti  
liveness gc G1GC 区域活跃度(死对象与活对象)
load gc,class,redefine 跟踪所有加载的类,包括重新定义的类。
loader class 类加载器data和constraints
logging   关于统一日志记录
malloc malloc 记录任何 C stdlib或调用,总是通过malloc标签记录。realloc free malloc
mark redefine 期间标记obsolete方法redefine
marking gc G1GC、CMS、ZGC的标识信息
membername   重定义用来替换方法的哈希表。与 结合使用table,但没有redefine?日志标记 root。
memops perf 内存操作日志initialize和mmap_attach_shared
metadata jfr,redefine 当日志标记根为时,有关类重新定义的日志redefine。或有关 JFR 事件元数据的日志,当日志标记根为jfr.
metaspace gc 关于元空间内存空间的日志
methodcomparator redefine 重新定义期间有关方法等效的日志。
mirror cds cds关于类镜像处理的日志。java 镜像是java.lang.Class.
mmu gc Minimum Mutator Utilization是 G1GC 和 ZGC 的 GC 工作的(时间)目标。G1GC 将此定义为MaxGCPauseMillis时间片 ( ) 上的暂停时间目标 (GCPauseIntervalMillis)。
module   模块系统
monitorinflation   运行时同步支持
monitormismatch   在 OopMapGeneration 期间监控匹配失败
nestmates class,redefine Nestmate 属性日志记录,使用日志标记根?class或redefine.
nmethod gc,class,redefine 本机方法(如在 Java 方法的编译代码版本中)记录在各种子系统中:gc、classredefine
normalize redefine 重定义过程中方法添加、删除、替换后的日志
objecttagging jvmti JVMTI 对象标记调用
obsolete redefine 主要用于重定义(和 JVMTI)的过时方法入口
oldobject jfr 旧对象采样,当前仅记录由于锁争用而跳过的示例。日志标记根是jfr.
oom gc metaspace内存不足时报告。
oopmap interpreter,redefine 记录普通对象指针缓存,结合 byinterpreter和redefine.
oops gc CMS和ZGC中的普通对象指针日志
oopstorage   用于管理对 Java 堆中分配的对象的引用的内部堆外数据结构
os os 操作系统交互日志。
pagesize   有关页面大小的日志。独立标签。
parser jfr 读取录音时在 JFR 解析器中使用的事件。日志标记根是jfr.
patch module 日志记录–patch-module
path class 类路径或模块路径处理
perf perf 有关 JVM 性能计数器的日志。但是当与它结合使用时,stringtable它是关于字符串 (String.intern()) 的。
phases gc 作为 GC 阶段的一部分的日志,特别是对于 ZGC 和 G1GC。
plab gc 有关每个 GC 线程分配缓冲区的日志,这些日志在 GC 期间用于防止线程在同一内存空间上竞争。
preorder class 跟踪按引用顺序加载的所有类(未加载)
preview   预览特征类型的跟踪加载
promotion gc GC 期间的对象提升日志。
protectiondomain class 类保护域验证。
purge redefine 重新定义期间有关先前类版本卸载的日志。
redefine redefine 类重定义子系统,特别是用于 JVMTI(例如调试时)和使用 Java 代理进行类转换/重新转换的子系统。
ref gc GC对象或类引用相关日志
refine gc G1GC 记录 RSet (Remembered set) 的细化线程
region gc G1GC 关于区域的日志。级别的allocandheap组合debug将打印区域统计信息,trace级别记录 GC 在区域上执行的操作和更多详细信息。
reloc gc ZGC 对象集重定位。
remset gc G1GC 记忆组。如果 level 是,trackingandexit标签可以出现trace。
resolve class,aot 关于常量池分辨率的日志,例如在加载类、lambda、进行反射、jni 时
safepoint safepoint JVM stop-the-world 操作 Heap Dump、一些 GC 任务、反优化、偏向锁撤销、库加载等……安全点虽然)。具体的操作日志可以用standalone?vmoperation.
sampling jfr JFR 中的旧对象采样。trace仅水平。
scavenge gc CMS、并行和串行 GC 清除跟踪。
setting jfr 的一部分jfr,主要是关于 JFR 配置。
smr thread 线程安全内存回收(Thread-SMR),危险指针。src/hotspot/share/runtime/threadSMR.cpp。
stackmap redefine 重新定义期间方法堆栈图中的常量池更改
stacktrace   期间记录Throwable.fillInStackTrace()。
stackwalk   使用时的日志StackWalker。
start gc,codecache 表示 ZGC 阶段开始,因此与gc.?但也可以指示其他操作开始,例如 for?codecache。
startuptime   记录 JVM 子系统的启动时间(解释器、GC、模块系统、类加载、aot、关于内存区域的创世……)
state gc CMS GC 状态。
stats gc GC 统计日志。也可以看到(oopstorage, blocks标签集)。
stringdedup gc G1GC 字符串去重(老年代)
stringtable   当 G1GC 用于记录字符串和符号清理时String.intern(),可以在gc?日志标记根中看到关于实习字符串 ( )。
subclass class,redefine 子类卸货。重新定义期间受影响的子类。
survivor gc CMS GC 幸存者信息。
sweep gc,codecache CMS GC 清扫活动。并且代码缓存本地方法根据日志标记 root刷新。
system jfr JFR 系统记录、记录开始/停止、紧急转储等。
table membername 仅与membername, 关于哈希表一起使用来记录方法,并在重新定义时替换它们。”
task gc 主要与 GC 任务相关,查看phases标签很有用。还与线程本地握手结合使用handshake。
thread os 当用作日志标签根时,它是关于smr,当它与os日志标签根结合使用时,它是关于线程生命周期和守卫的。
time gc 定时并行 GC 操作
timer thread,redefine 定时重定义操作和线程退出
tlab gc 线程本地分配缓冲区统计信息 (trace)
tracking gc G1GC 记忆集跟踪
unload class 类或模块卸载
unshareable cds 对于无法共享的类,在 CDS 存档创建期间特别有趣
update redefine 有关类、方法、常量池、vtable、itable 等的重新定义更改的日志。
verification   类字节码验证器,当独立时。与cds.
verify gc 在 GC 操作期间完成的验证操作。
vmoperation   记录 VM 操作
vmthread   执行 VM 操作的 JVM 线程(通常在安全点期间)
vtables   Java 的虚拟调用机制(虚拟表允许在类层次结构中为当前实例找到正确的方法地址)。以前学过 C++ 的人会记得。
vtablestubs   Java 的虚拟调用机制,用于多态调用站点(即,由于对象层次结构不同,在连续执行中执行的方法不同时)。热点维基1?2
workgang gc GC 工作线程。

日志输出方式

支持stdout、stderr和file三种输出方式。

stdout/stderr没什么特殊的。file增强了很多,文件名中包含 %p和%t 会被替换为 进程标识符和时间戳。 filesize设置按照文件大小滚动日志,filecount设置滚动日志文件的数量。

1
...:file=/var/log/gc-%t.log:time,tags,level:filecount=5,filesize=10M

这段参数含义是输出日志文件gc-2022-10-20_16-07-18.log, 使用time tags level装饰器, 文件每10MB拆分一个新日志文件,最大保持5个日志文件。

开启日志异步写入

-Xlog:async启用异步日志写入,减少写日志导致的应用线程阻塞。

示例

1
2
3
4
5
6
7
8
9
10
11
12
-XX:+PrintGCDetails                           \
-XX:+PrintGCApplicationStoppedTime            \
-XX:+PrintGCApplicationConcurrentTime         \
-XX:+PrintGCCause                             \
-XX:+PrintGCID                                \
-XX:+PrintClassHistogram                      \
-XX:+PrintTenuringDistribution                \
-XX:+PrintGCDateStamps                        \
-XX:+UseGCLogFileRotation                     \
-XX:NumberOfGCLogFiles=5                      \
-XX:GCLogFileSize=10M                         \
-Xloggc:/var/log/`date +%FT%H-%M-%S`-gc.log   \
1
2
3
4
5
6
7
8
9
10
11
12
-Xlog:
  gc*,
  gc+heap=debug,
  gc+ref=debug,
  gc+ergo*=trace,
  gc+age*=trace,
  gc+phases*=debug,
  classhisto*=debug,
  safepoint*
  :file=/var/log/%t-gc.log
  :time,tags,level
  :filecount=5,filesize=10M

参数 -verbose:gc

1
gc=trace gc+heap=trace gc+heap+exit=trace gc+metaspace=trace gc+sweep=debug gc+heap+ergo=debug

参数 -XX:+PrintGCTimeStamps

等同于启用uptime装饰器

参数 -XX:+PrintClassHistogram

等同于classhisto*=trace

参数 -XX:+PrintHeapAtGC

等同于gc+heap=debug

1
2
3
4
5
6
7
8
9
10
11
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2) Min Capacity: 512M(50%)
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2) Max Capacity: 1024M(100%)
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2) Soft Max Capacity: 1024M(100%)
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2)                Mark Start          Mark End        Relocate Start      Relocate End           High               Low         
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2)  Capacity:      512M (50%)         512M (50%)         512M (50%)         512M (50%)         512M (50%)         512M (50%)    
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2)      Free:      700M (68%)         652M (64%)         702M (69%)         894M (87%)         894M (87%)         650M (63%)    
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2)      Used:      324M (32%)         372M (36%)         322M (31%)         130M (13%)         374M (37%)         130M (13%)    
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2)      Live:         -                39M (4%)           39M (4%)           39M (4%)             -                  -          
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2) Allocated:         -                48M (5%)           52M (5%)           55M (5%)             -                  -          
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2)   Garbage:         -               284M (28%)         230M (22%)          34M (3%)             -                  -          
[2022-10-20T16:07:27.311+0800][20044][gc,heap     ] GC(2) Reclaimed:         -                  -                54M (5%)          249M (24%)            -                  -          

参考资料

  1. Java 17 JVM logging framework
  2. Embracing JVM unified logging

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 TinyZ Zzh (包含链接: https://tinyzzh.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。 如有任何疑问,请 与我联系 (tinyzzh815@gmail.com)

TinyZ Zzh

TinyZ Zzh

专注于高并发服务器、网络游戏相关(Java、PHP、Unity3D、Unreal Engine等)技术,热爱游戏事业, 正在努力实现自我价值当中。

评论

  点击开始评论...