JVM 统一的日志框架

从JDK9开始,JVM引入了统一的日志框架,用于集中管理包括GC日志在内的各种日志。通过-Xlog参数,我们可以更灵活地配置日志输出,例如输出位置、文件大小、日志级别和标签。动态调整日志输出可使用jcmdVM.log命令,允许在运行时改变日志配置。文章提供了示例展示如何设置和使用这些选项,以及如何通过标签选择特定模块的日志并控制其级别。
部署运行你感兴趣的模型镜像

引言

对于基于 Java 开发的应用程序而言, GC 日志是我们了解 JVM 内存使用的情况的一个重要信息源, 所以我们在生产环境都打开 GC 的日志. 在 JDK 8 之前, 我们都使用诸如: -XX:+PrintGCDetails-XX:+PrintGCDateStamps-XX:+UseGCLogFileRotation-XX:NumberOfGCLogFiles=5, 等这些 JVM 启动选项, 然而, 自从 JDK 9 开始, 我们却开始使用另外一套 JVM 启动选项, 如: -Xlog:gc*=debug:file=/tmp/gc.log:uptime,tid:filecount=5,filesize=2m 来设置 GC 的日志相关的配置.

为什么同样是设置 GC 的日志配置, 却有了不同的 JVM 启动选项呢? 这是因为: 自从 JDK 9 开始, JVM 有了一套统一的日志框架(JVM Unified Logging Framework). GC 日志作为 JVM 输出的一种日志, 也可以通过这个统一的日志框架来配置. 虽然 JVM 还接受原来的启动选项, 但是长久来看, 所有的这些跟日志相关的选项, 最终都将只保留通过这个统一的日志框架来配置.

什么是 JVM 统一的日志框架

JVM 统一的日志框架(JVM Unified Logging Framework)是 JDK 9 引入的一个中心化的日志框架, 通过这个日志框架, 我们可以观测 JVM 的 类加载(class loading), 线程(thread), 垃圾收集(GC), 模块系统(Module System) 等相关信息. 我们可以通过设置设置 JVM 启动参数 -Xlog 来与这个统一的日志框架交互, 从而让这个统一的日志框架输出不同组建的, 不同层级(level)的日志到我们指定的日志文件.

除了通过 -Xlog 这个启动参数外, 我们也可以在运行时实时修改统一日志框架的输出. 一种方法是通过 jcmd 命令的 VM.log 子命令来修改, 另外一种方法是通过修改对应的 MBean. -Xlog 是在应用启动应用时候修改, 后面2种都是运行时动态修改.

后面的例子中, 为了举例方便, 我们都使用 jcmd VM.log 动态修改, 然后观察修改的结果.

简单的例子

假设我们有一个正在运行的 Java 应用, 它的进程号(pid)是 3499:

$ jps
16527 Jps
3499 Pycharm
复制代码

我们把所有的日志都打印到 /tmp/my.log 文件去, 可以这么操作:

$ jcmd 3499 VM.log output="file=/tmp/my.log" what="all=trace"
3499:
Command executed successfully

$ tail -f /tmp/my.log
[238674.318s][debug][gc,task               ] G1 Service Thread (Remembered Set Sampling Task) (run) 0.330ms (cpu: 0.327ms)
[238674.318s][trace][gc,task               ] G1 Service Thread (wait) 0.300s
[238674.333s][trace][monitorinflation,owner] try_set_owner_from(): mid=0x000060000089e220, prev=0x0000000000000000, new=0x00007fc6860d8c00
[238674.333s][trace][monitorinflation,owner] release_clear_owner(): mid=0x000060000089e220, old_value=0x00007fc6860d8c00
[238675.209s][trace][monitorinflation,owner] try_set_owner_from(): mid=0x0000600014b634d0, prev=0x0000000000000000, new=0x00007fc607a54a00
[238675.209s][trace][monitorinflation,owner] release_clear_owner(): mid=0x0000600014b634d0, old_value=0x00007fc607a54a00
复制代码

上面的代码中, 我们先是把所有的日志都通过 jcmd VM.log 的方式输出到 /tmp/my.log 文件中, 然后通过 tail 命令查看最新的日志内容.

jcmd的命令中, 3499 是 Java 应用进程号, VM.log 是 jcmd的子命令, 后面的是 VM.log 子命令的选项, 我们的注意力主要集中在选项这一块.

日志输出到哪

在这个简单的例子中, 我们设置了 output="file=/tmp/my.log", 主要用来设置输出到哪里, 我们可以设置为一个文件, 或者 stdout, stderr:

$ jcmd 3499 VM.log output="stdout" what="all=trace"
$ jcmd 3499 VM.log output="stderr" what="all=trace"
复制代码

当然, 这里的 stdout 和 stderr 都是进程 3499 对应的 stdout 和 stderr(Linux系统每个进程都有自己单独的stdout, stderr伪文件), 并不是当前的控制台.

所以, 总结一下 output, 它有3种可能, 分别是:

  1. stdout
  2. stderr
  3. file=<file_name> 当使第三种 file=<file_name> 的时候, 我们可以设置文件名里面包含%p%t, 分别代表应用进程号和应用启动的时间.

日志文件的大小和多少

对于上面的第三种写到文件系统的, 一般都能设置rotate, 就是文件轮转. 比如最多写5个文件, 每个文件最大6M这种. JVM 统一的日志框架也支持这些设置, 它通过选项 output_options 设置. 比如:

$ jcmd 3499 VM.log output="stdout" what="all=trace" output_options="filecount=5,filesize=6M"
复制代码

可以看到, 我们设置了文件最多是5个, 文件大小最大是6M. 文件大小的单位可以是 K, M, G.

那么如果设置 filecount=0 呢? 这就代表告诉系统, 不要rotate 日志文件.

写哪些模块的日志到日志文件

其实一开始到现在, 我们都没有聊选项中的 what="all=trace", 这部分选项告诉日志系统, 什么东西要写到日志文件里面. 我们给的内容是 all=trace, 那它表示什么呢?

日志的层级

通常我们写日志的方式大概是这样的:

log.info("this is a info log");
log.error("this is an error log"
复制代码

这里我们分别写了一条 info level 和 一条 error level 的日志. 这里的 info, error 都是日志的级别, 同样, JVM 统一的日志框架中, 也有几个级别: offtracedebuginfowarningerror. 这其中 off 最特别, 它其实是告诉日志框架, 全闭嘴, 相当于关掉所有层级的日志. 可以看到我们上面的例子中都是使用的 trace level, 是级别最低的, 等于输出所有的层级的日志.

日志的标签(tag)

一开始的时候, 我们说到这个统一的日志框架可以输出不同模块的日志,比如GC, 线程, classload等模块. 那么我们怎么选择那些日志我们能输出, 那些不让它输出呢?

为了让我们有选择的输出, 需要一个选择标准, 这里使用的就是标签. 日志框架在输出日志的时候, 给每一条日志都加了一个或多个标签, 我们可以选择不同的标签组合, 选择不同的日志内容输出到日志文件.

JVM 统一的日志框架写日志的方式类似于这样, 每一行日志都包含一些标签:

log.info("this is a info log", ["tag1", "tag2"]);
log.error("this is an error log", ["tag2", "tag3"]);
复制代码

那么我们和统一的日志框架交互的时候, 就可以根据这些标签去选择到底哪些日志要输出. 我们可以通过命令来看当前的 JVM 支持哪些标签:

$ jcmd 3499 VM.log list 
3499:
Available log levels: off, trace, debug, info, warning, error
Available log decorators: time (t), utctime (utc), uptime (u), timemillis (tm), uptimemillis (um), timenanos (tn), uptimenanos (un), hostname (hn), pid (p), tid (ti), level (l), tags (tg)
Available log tags: add, age, alloc, annotation, arguments, attach, barrier, biasedlocking, blocks, bot, breakpoint, bytecode, cds, census, class, classhisto, cleanup, codecache, compaction, compilation, condy, constantpool, constraints, container, coops, cpu, cset, data, datacreation, dcmd, decoder, defaultmethods, director, dump, dynamic, ergo, event, exceptions, exit, fingerprint, free, freelist, gc, handshake, hashtables, heap, humongous, ihop, iklass, indy, init, inlining, install, interpreter, itables, jfr, jit, jni, jvmci, jvmti, lambda, library, liveness, load, loader, logging, malloc, map, mark, marking, membername, memops, metadata, metaspace, methodcomparator, methodhandles, mirror, mmu, module, monitorinflation, monitormismatch, nestmates, nmethod, nmt, normalize, numa, objecttagging, obsolete, oldobject, oom, oopmap, oops, oopstorage, os, owner, pagesize, parser, patch, path, perf, periodic, phases, plab, placeholders, preorder, preview, promotion, protectiondomain, ptrqueue, purge, record, redefine, ref, refine, region, reloc, remset, resolve, safepoint, sampling, scavenge, sealed, setting, smr, stackbarrier, stackmap, stacktrace, stackwalk, start, startup, startuptime, state, stats, streaming, stringdedup, stringtable, subclass, survivor, suspend, sweep, symboltable, system, table, task, thread, throttle, time, timer, tlab, tracking, unload, unshareable, update, valuebasedclasses, verification, verify, vmmutex, vmoperation, vmthread, vtables, vtablestubs, workgang
Described tag sets:
 logging: Logging for the log framework itself
Log output configuration:
 #0: stdout all=trace uptime,level,tags (reconfigured)
 #1: stderr all=off uptime,level,tags
 #2: file=/tmp/gc.log all=trace time,level,tags filecount=5,filesize=10240K,async=false (reconfigured)
 #3: file=/tmp/my.log all=trace uptime,level,tags filecount=5,filesize=20480K,async=false (reconfigured)
复制代码

其中 Available log tags 包含了所有支持的tag. 而我们上面使用的 all 则表示包含所有的tags.

所以这也是 what 所表达的含义, 它通过 tags 和日志 level 一起来选择什么(what)日志应该被输出出来. 比如: gc=debugjfr=traceinit=info 等.

日志每行的格式

在一开始的例子中, 我们看到了如下的日志行:

[238674.318s][trace][gc,task ] G1 Service Thread (wait) 0.300s
复制代码

这跟我们平时看到的日志很像, 第一个方括号里面的表示服务器从开始启动到打日志的时间, 第二个方括号里面表示日志的层级, 第三个表示日志的tags, 最后是日志的内容.

上面的输出中是默认的日志每一行的格式, 其实我们可以定制这个格式, 并且加减一些内容.

首先, 除了时间,日志level, tags, 我们还有哪些可以跟日志一块打印的东西呢? 这些可以和日志内容一块输出的叫: decorator, 从之前的jcmd <pid> VM.log list的输出中, 我们可以看到有如下的 decorators:

Available log decorators: time (t), utctime (utc), uptime (u), timemillis (tm), uptimemillis (um), timenanos (tn), uptimenanos (un), hostname (hn), pid (p), tid (ti), level (l), tags (tg)
复制代码

其中括号里面的是简写的形式. 主要是各种形式的时间表示, 主机名, 进程号, 线程号, 日志层级, 标签.

所以, 到此我们可以写一个完整的, 包含上面所有涉及到的jcmd VM.log的命令:

$ jcmd 3499 VM.log output="file=/tmp/my.log" what="class=debug" decorators="t,hn,p,ti,l,tg" output_options="filecount=5,filesize=2M"
复制代码

选项的默认值

对于上面的选项outputoutput_optionswhatdecorators 我们可以省略, 如果省略, 则使用的它们的默认值. 它们的默认值分别是:

  1. what: "all=info"
  2. output: "stdout"
  3. output_options: ""
  4. decorators: "uptime,level,tags"

注意上面的 what, 如果单单省略 what, 默认值是 “all=info“, 如果是使用启动参数 -Xlog, 后面没有给任何选项, 那么等价于: -Xlog:all=warning:stdout:uptime,level,tags

稍微复杂的例子

上面的选项的内容都比较简单, 要么是选择所有的tag, 要么是选择一个tag, 如果我们要选择多个tag 怎么办呢?

选择多个tags

tag 之间可以使用逗号隔开, 这样就能选择多个tags, 只要日志标签中包含任何一个这样的标签都会被输出, 比如:

$ jcmd 3499 VM.log what="gc,class,safepoint=debug"
复制代码

tag 的模糊匹配

我们可以使用*来模糊匹配标签(tag), 这样匹配任何以gc开头的标签, 比如:

$ jcmd 3499 VM.log what="gc*=debug"
复制代码

tag 的叠加

我们可以设置当一个日志行中包含所有我们要求的标签才能输出, 比如:

$ jcmd 3499 VM.log what="gc+oom"
复制代码

这样, 当一行日志即包含gc标签, 又包含oom标签, 同时日志的level是info及以上的时候, 才会打印出来.


这里要注意: 没有被显示指明的标签(tag), 只要是warning及以上, 同样会被输出到日志文件


只输出我选择的标签

如果要关闭其它不想看到的标签日志, 则需要先使用disable=true, 然后在enable其它标签, 如下:

$ jcmd 3499 VM.log disable=true output="file=/tmp/my.log" what="gc=trace" 
复制代码

多个标签不同的日志层级

如果要给不同的标签不同的日志层级, 可以使用下面的方式:

$ jcmd 3499 VM.log what="gc=trace,class=error,threa*=info,class*=off"
复制代码

总结

通过上面的例子, 我们看到有3种方法与 JVM 统一的日志框架打交道:

  1. 启动参数 -Xlog 适用于启动时候设置
  2. 使用 jcmd <pid> VM.log 可以动态调整日志的输出
  3. 使用 MBean, 可以动态调整, 没有 jcmd 命令方便

打印日志的选项主要有4个:

  1. what: 选择标签(tags)和日志层级
  2. output: 选择输出到哪里 stdoutstderr 或者文件
  3. output_options: 如果选择输出到文件, 文件的一些选项, 比如大小和rotate的多少
  4. decorators: 日志行除了日志内容外, 还要加哪些信息.

如果想查看帮助和所有的选项内容, 可以尝试下面的命令:

$ jcmd <pid> help VM.log
$ jcmd <pid> VM.log list
复制代码

希望帮助到需要的人.

 

您可能感兴趣的与本文相关的镜像

Linly-Talker

Linly-Talker

AI应用

Linly-Talker是一款创新的数字人对话系统,它融合了最新的人工智能技术,包括大型语言模型(LLM)、自动语音识别(ASR)、文本到语音转换(TTS)和语音克隆技术

<think>嗯,用户想了解JVMGC日志的参数配置,这涉及JVM调优的关键部分。结合三篇引用内容,特别是新版JVM的变化,可以整理出清晰的配置方案。引用1提到G1GC配置示例和日志输出路径,引用2详细对比了Java9前后日志参数的差异,引用3补充了元数据区等重要参数。这说明需要区分Java版本讲解,用户可能正在迁移旧项目或优化生产环境配置。注意到用户明确要求"详解",准备从日志框架演变讲起。考虑划分模块说明:基础参数(输出开关/路径)、格式控制(时间戳/级别)、高级选项(堆/元空间)和版本适配策略。对于Java9+要重点解释-Xlog指令的灵活性,比如使用装饰器添加附加信息的方式可能正是用户需要的。示例部分将展示两种典型场景:开发环境调试用详细GC日志(带时间戳和堆信息),生产环境轻量日志(仅记录关键事件)。考虑引用2里的可视化工具GcViewer也应该包含在后续问题中,用户分析日志时可能需要。用户应该是后端开发或运维,三篇引用都偏向实战,推测更关注具体参数效果而非理论。准备避免过多GC算法原理解释,直接关联参数设置与实际输出效果的关系。比如-XX:+PrintAdaptiveSizePolicy对于排查G1动态调整特别有用。考虑到存在版本兼容问题,结尾决定添加版本适配说明。这样用户无论使用Java8还是11+都能直接套用模板。后续问题设置了参数优化和异常排查方向,这和调优的实际工作流程一致:先配日志再看数据最后解决问题。</think> 以下是基于Java不同版本的JVM GC日志参数详解,结合核心配置项和实际示例说明: --- ### **一、GC日志核心参数配置** #### **1. 日志输出基础控制** | 参数 | 作用 | 适用版本 | |------|------|----------| | `-Xlog:gc*` | 开启所有GC事件日志记录 | Java 9+ | | `-XX:+PrintGC` | 输出简要GC信息 | Java 8- | | `-XX:+PrintGCDetails` | 输出详细GC堆内存变化 | Java 8- | | `-Xlog:gc:gc.log` | 将日志输出到gc.log文件 | Java 9+ | | `-XX:+PrintGCDateStamps` | 添加时间戳(需配合`PrintGC`) | Java 8- | #### **2. 日志格式增强** | 参数 | 作用 | |------|------| | `-Xlog:gc*,time:gc_time.log` | 添加精确时间戳(Java 9+)<sup>[^2]</sup> | | `-Xlog:gc*:file=gc.log:tags,uptime` | 日志添加线程/时间信息(Java 9+) | | `-XX:+PrintTenuringDistribution` | 打印对象年龄分布(用于分析晋升行为) | #### **3. 堆与GC算法参数** ```bash -XX:+UseG1GC # 启用G1收集器 -XX:MaxGCPauseMillis=100 # 目标最大停顿时间(ms) -Xms512m -Xmx512m # 堆初始/最大内存 -XX:MetaspaceSize=128m # 元空间初始大小(Java 8+取代永久代) -XX:MaxMetaspaceSize=256m # 元空间上限<sup>[^3]</sup> ``` --- ### **二、不同版本配置示例** #### **▶ Java 8及之前** ```bash -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -XX:MaxMetaspaceSize=256m ``` #### **▶ Java 9及之后(推荐)** ```bash -Xlog:gc*,gc+heap*,gc+age*:file=gc.log:time,level,tags -XX:+UseG1GC -XX:MaxGCPauseMillis=150 -Xms512m -Xmx512m ``` --- ### **三、日志关键内容解析** **示例日志片段(G1 GC)**: ```log [2023-10-01T12:00:00.123+0800][info][gc,heap] GC(42) Eden regions: 50->0(50) [info][gc,heap] GC(42) Survivor regions: 5->5(10) [info][gc,heap] GC(42) Old regions: 30->35 [info][gc,heap] GC(42) Humongous regions: 1->1 [info][gc ] GC(42) Pause Young (Normal) 25ms ``` - **Eden/Survivor**:年轻代区域变化 - **Old regions**:老年代占用增长 - **Pause Young**:年轻代GC停顿时间 --- ### **四、可视化分析工具** 1. **GcViewer**:开源工具,解析GC日志生成图表 ![GC暂停时间直方图示例](https://gcviewer.org/img/screen1.jpg) (可分析暂停时间分布、内存趋势等) 2. **通用日志分析方法**: - 高频Full GC → 检查老年代/元空间设置 - 长暂停时间 → 调整`MaxGCPauseMillis`或堆大小 - Survivor区频繁清零 → 对象过早晋升 --- ### **五、版本适配注意** - **Java 9+** 使用`-Xlog`统一日志框架,废弃`PrintGCDetails`等旧参数<sup>[^2]</sup> - **Java 17+** 默认G1收集器,建议显式配置`-XX:+UseG1GC`保持兼容性 --- ### **相关问题扩展** 1. 如何分析GC日志中的 **"Allocation Failure"** 报错? 2. G1与ZGC的日志参数配置有何差异? 3. 生产环境如何平衡 **详细日志记录** 与 **磁盘IO开销**? 4. 怎样通过GC日志判断 **内存泄漏**? > 参考资料: > [^1]: Java高阶私房菜:GC日志实战配置 > [^2]: jvm-47: GC日志获取与GcViewer分析 > [^3]: JVM元空间参数详解
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值