详解Java的 统一JVM日志框架

3084 字
15 分钟
详解Java的 统一JVM日志框架

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

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

统一JVM日志框架#

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

通过java -Xlog查看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…相关的参数缺省值都是false, 并且推荐使用log进行配置。

模块日志标签描述
addredefine重新定义方法时的日志。
agegcGC 任期分布及相关统计。
allocgcGC 区域或空间分配。
annotationredefine重新定义期间有关注释的日志,此标记似乎仅与redefine.
aotaot提前机制日志。
argumentsJVM 选项警告或错误(只有错误,除非 level 是trace),例如,当使用应使用统一日志记录而不推荐使用的日志记录选项时。
attachJVM 附件监听器
barriergc,xxxGC 写屏障覆盖率
biasedlocking偏向锁定机制src/hotspot/share/runtime/biasedLocking.hpp
blocksoopstorage,gc
botgc块偏移表(带有偏移信息的侧数据结构,指示 GC 需要回溯多远才能找到对象的开头)。
breakpointjvmti(重新定义)断点设置或清除 JVMTI 的一部分,但记录在redefine?log 标记 root下。
bytecodejfr有关由 执行的字节码修改的日志JFR,始终与jfr.
cdscds类数据共享日志
censusgcCMS 日志freelist信息。
class,class,aot类相关信息
classhistogc
cleanupsafepoint安全点清洁信息
codecachecodecache,compilation生成代码的代码缓存:已编译的 java 方法、运行时存根、转换帧等。隔离在 3 个堆中(非 nmethods,如缓冲区、适配器和运行时存根,已分析的 nmethods,未分析的 nmethods)。src/hotspot/share/code/codeCache.cpp
compactiongcG1GC 和 Parallel 的 GC 压缩
compilationcompilation,jit汇编
constantpoolclass,redefine,常量池,主要在redefine
constraintsclass类加载器约束
containeros容器 (CGroup) 相关 (cpu,cpuset,memory,mountinfo等等)
coopsgc压缩普通对象指针
cpugc,os记录时的 CPU 特性os,CPU 时间 igc
csetgcG1中的收藏集
dataclass类加载器数据(分配空间或元空间)
datacreationperf与 perf 数据创建相关
dcmdjfrJFR 诊断命令日志始终与日志标记根结合在一起jfr
decoder与 Linux 上的 ELF 解码(可执行和可链接格式)相关。
defaultmethods默认方法的运行时支持。类的一部分,但作为独立标签记录。
directorgcZGC相关
dumpredefine重新定义后重新定义有关旧方法或过时方法的调试日志记录。
ergogc人体工程学,通常与 结合gc,但可以单独找到(例如,关于线程本地握手的 2 个日志)
eventjfr关于 JFR 事件
exceptionsexceptions,redefine关于不同部分(解释器、安全点期间、编译器……)中的 Java 异常处理。与redefine.
exitgcGC 之后的 GC/heap 相关信息。LogStreamHandle(Debug, gc, heap, exit)
fingerprintclass,aot类指纹
freemalloc记录任何 C stdlib或调用,总是通过malloc标签记录。realloc free malloc, free
freelistgc应该是空闲的内存块,用于 CMS 和元空间
gcgcGC 日志标签根。
handshakethread线程本地握手src/hotspot/share/runtime/handshake.hpp
hashtables独立时,它是内部分桶哈希表,是 a 的远亲HashMap,Hotspot 将它用于字符串和其他符号。CDS 使用另一个内部实现进行统计,因此该标签可以与cds?日志标签 root结合使用。
heapgc各种GC堆相关的日志
humongousgcG1GC 中的巨大对象
ihopgcG1GC 中的堆占用(初始、当前、目标)
iklassclass,redefine内部类实例表示,日志标签根:redefine,classes
initgc,classlog tag root主要是关于什么时候的类初始化class。关于结合gc.
inliningjit方法内联信息
interpreterinterpreter解释日志,只记录带有oopmap和safepoint子标签的日志。
itablesJVM 开发过程中接口调用的 JVM 虚拟调用机制(需要非产品构建)。另见vtables标签。热点维基
jfrjfrJDK飞行记录器
jitjitinlining关于方法和的日志compilation
jnigc仅在GC locker期间记录。
jvmtijvmti
livenessgcG1GC 区域活跃度(死对象与活对象)
loadgc,class,redefine跟踪所有加载的类,包括重新定义的类。
loaderclass类加载器data和constraints
logging关于统一日志记录
mallocmalloc记录任何 C stdlib或调用,总是通过malloc标签记录。realloc free malloc
markredefine期间标记obsolete方法redefine
markinggcG1GC、CMS、ZGC的标识信息
membername重定义用来替换方法的哈希表。与 结合使用table,但没有redefine?日志标记 root。
memopsperf内存操作日志initialize和mmap_attach_shared
metadatajfr,redefine当日志标记根为时,有关类重新定义的日志redefine。或有关 JFR 事件元数据的日志,当日志标记根为jfr.
metaspacegc关于元空间内存空间的日志
methodcomparatorredefine重新定义期间有关方法等效的日志。
mirrorcdscds关于类镜像处理的日志。java 镜像是java.lang.Class.
mmugcMinimum Mutator Utilization是 G1GC 和 ZGC 的 GC 工作的(时间)目标。G1GC 将此定义为MaxGCPauseMillis时间片 ( ) 上的暂停时间目标 (GCPauseIntervalMillis)。
module模块系统
monitorinflation运行时同步支持
monitormismatch在 OopMapGeneration 期间监控匹配失败
nestmatesclass,redefineNestmate 属性日志记录,使用日志标记根?class或redefine.
nmethodgc,class,redefine本机方法(如在 Java 方法的编译代码版本中)记录在各种子系统中:gc、classredefine
normalizeredefine重定义过程中方法添加、删除、替换后的日志
objecttaggingjvmtiJVMTI 对象标记调用
obsoleteredefine主要用于重定义(和 JVMTI)的过时方法入口
oldobjectjfr旧对象采样,当前仅记录由于锁争用而跳过的示例。日志标记根是jfr.
oomgcmetaspace内存不足时报告。
oopmapinterpreter,redefine记录普通对象指针缓存,结合 byinterpreter和redefine.
oopsgcCMS和ZGC中的普通对象指针日志
oopstorage用于管理对 Java 堆中分配的对象的引用的内部堆外数据结构
osos操作系统交互日志。
pagesize有关页面大小的日志。独立标签。
parserjfr读取录音时在 JFR 解析器中使用的事件。日志标记根是jfr.
patchmodule日志记录—patch-module
pathclass类路径或模块路径处理
perfperf有关 JVM 性能计数器的日志。但是当与它结合使用时,stringtable它是关于字符串 (String.intern()) 的。
phasesgc作为 GC 阶段的一部分的日志,特别是对于 ZGC 和 G1GC。
plabgc有关每个 GC 线程分配缓冲区的日志,这些日志在 GC 期间用于防止线程在同一内存空间上竞争。
preorderclass跟踪按引用顺序加载的所有类(未加载)
preview预览特征类型的跟踪加载
promotiongcGC 期间的对象提升日志。
protectiondomainclass类保护域验证。
purgeredefine重新定义期间有关先前类版本卸载的日志。
redefineredefine类重定义子系统,特别是用于 JVMTI(例如调试时)和使用 Java 代理进行类转换/重新转换的子系统。
refgcGC对象或类引用相关日志
refinegcG1GC 记录 RSet (Remembered set) 的细化线程
regiongcG1GC 关于区域的日志。级别的allocandheap组合debug将打印区域统计信息,trace级别记录 GC 在区域上执行的操作和更多详细信息。
relocgcZGC 对象集重定位。
remsetgcG1GC 记忆组。如果 level 是,trackingandexit标签可以出现trace。
resolveclass,aot关于常量池分辨率的日志,例如在加载类、lambda、进行反射、jni 时
safepointsafepointJVM stop-the-world 操作 Heap Dump、一些 GC 任务、反优化、偏向锁撤销、库加载等…安全点虽然)。具体的操作日志可以用standalone?vmoperation.
samplingjfrJFR 中的旧对象采样。trace仅水平。
scavengegcCMS、并行和串行 GC 清除跟踪。
settingjfr的一部分jfr,主要是关于 JFR 配置。
smrthread线程安全内存回收(Thread-SMR),危险指针。src/hotspot/share/runtime/threadSMR.cpp。
stackmapredefine重新定义期间方法堆栈图中的常量池更改
stacktrace期间记录Throwable.fillInStackTrace()。
stackwalk使用时的日志StackWalker。
startgc,codecache表示 ZGC 阶段开始,因此与gc.?但也可以指示其他操作开始,例如 for?codecache。
startuptime记录 JVM 子系统的启动时间(解释器、GC、模块系统、类加载、aot、关于内存区域的创世……)
stategcCMS GC 状态。
statsgcGC 统计日志。也可以看到(oopstorage, blocks标签集)。
stringdedupgcG1GC 字符串去重(老年代)
stringtable当 G1GC 用于记录字符串和符号清理时String.intern(),可以在gc?日志标记根中看到关于实习字符串 ( )。
subclassclass,redefine子类卸货。重新定义期间受影响的子类。
survivorgcCMS GC 幸存者信息。
sweepgc,codecacheCMS GC 清扫活动。并且代码缓存本地方法根据日志标记 root刷新。
systemjfrJFR 系统记录、记录开始/停止、紧急转储等。
tablemembername仅与membername, 关于哈希表一起使用来记录方法,并在重新定义时替换它们。“
taskgc主要与 GC 任务相关,查看phases标签很有用。还与线程本地握手结合使用handshake。
threados当用作日志标签根时,它是关于smr,当它与os日志标签根结合使用时,它是关于线程生命周期和守卫的。
timegc定时并行 GC 操作
timerthread,redefine定时重定义操作和线程退出
tlabgc线程本地分配缓冲区统计信息 (trace)
trackinggcG1GC 记忆集跟踪
unloadclass类或模块卸载
unshareablecds对于无法共享的类,在 CDS 存档创建期间特别有趣
updateredefine有关类、方法、常量池、vtable、itable 等的重新定义更改的日志。
verification类字节码验证器,当独立时。与cds.
verifygc在 GC 操作期间完成的验证操作。
vmoperation记录 VM 操作
vmthread执行 VM 操作的 JVM 线程(通常在安全点期间)
vtablesJava 的虚拟调用机制(虚拟表允许在类层次结构中为当前实例找到正确的方法地址)。以前学过 C++ 的人会记得。
vtablestubsJava 的虚拟调用机制,用于多态调用站点(即,由于对象层次结构不同,在连续执行中执行的方法不同时)。热点维基1?2
workganggcGC 工作线程。

日志输出方式#

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

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

Terminal window
...: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启用异步日志写入,减少写日志导致的应用线程阻塞。

示例#

Terminal window
-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 \
Terminal window
-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#

Terminal window
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

Terminal window
[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

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
详解Java的 统一JVM日志框架
https://tinyzzh.github.io/posts/2022-10-20-jvm_xlog/
作者
TinyZ Zzh
发布于
2022-10-20
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
TinyZ Zzh
专注于高并发服务器、网络游戏相关(Java、PHP、Unity3D、Unreal Engine等)技术,热爱游戏事业, 正在努力实现自我价值当中。
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
211
分类
38
标签
200
总字数
337,853
运行时长
0
最后活动
0 天前

文章目录