详解Java的 统一JVM日志框架
JAVA 9引入的统一JVM日志框架,旨在解决JVM内部各种各种混乱的打印日志和配置, 增强日志框架的功能(e.g. 滚动日志文件,异步日志.etc)并简化配置。JEP-158、JEP-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%) - -
参考资料
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 TinyZ Zzh (包含链接: https://tinyzzh.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。 如有任何疑问,请 与我联系 (tinyzzh815@gmail.com) 。
评论