news 2026/9/13 12:58:29

JDK jinfo 命令深度解析:查询与修改运行中 Java 进程的系统属性与 JVM 参数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JDK jinfo 命令深度解析:查询与修改运行中 Java 进程的系统属性与 JVM 参数

JDK jinfo 命令深度解析:查询与修改运行中 Java 进程的系统属性与 JVM 参数

【免费下载链接】jdkJDK main-line development https://openjdk.org/projects/jdk项目地址: https://gitcode.com/GitHub_Trending/jd/jdk

jinfo是 JDK 随附的诊断命令之一,用于在不重启目标 JVM 的前提下查看(甚至动态修改)其 Java 系统属性与命令行标志。本文基于 JDK 仓库中jinfo命令的官方手册页(man page)源文件 src/jdk.jcmd/share/man/jinfo.md 展开,完整继承其命令语法、选项与平台限制说明,并结合仓库中该命令的实际实现源码——入口类 JInfo.java、进程匹配器 ProcessArgumentMatcher.java、Attach 通信层 HotSpotVirtualMachine.java 以及 HotSpot 服务端 attachListener.cpp——剖析每个选项背后真实的调用链。读完本文,你既能照文档正确地在生产环境中使用jinfo,也能从源码层面理解它如何通过 Attach 机制与目标 JVM 通信、哪些操作是受限的、以及报错信息从何而来。

命令定位:实验性、不受支持的诊断工具

手册页的 Name 节对jinfo的定义是:

jinfo - generate Java configuration information for a specified Java process

为指定的 Java 进程生成 Java 配置信息。

配置信息包含两大类内容:Java 系统属性(Java System Properties)与JVM 命令行标志(JVM command-line flags)。

需要特别注意手册页在 Synopsis 节的醒目标注:

Note:This command is experimental and unsupported.

该命令是实验性的且不受支持(unsupported),且“可能在 JDK 的后续版本中不再提供”。

这并非客套话,而是影响使用决策的事实边界:jinfo的功能与报错格式可能随版本变化,关键生产场景下不宜将其写入不可控的自动化脚本核心路径;同族的 jcmd.md 等命令文档可以说明,jcmd是目前官方更推荐的功能等价物(jinfo的大部分查询能力都可以用jcmd <pid> VM.flagsVM.system_propertiesVM.command_line完成,下文源码部分会看到二者共享同一底层通道)。

用法(Synopsis)

jinfo [option] pid

只有两个位置参数要素:

参数含义
optionjinfo的命令行选项,见下文“选项一览”
pid目标 Java 进程的进程 ID(必须是 Java 进程)。要列出本机正在运行的 Java 进程,可以使用ps命令,或者(在 JVM 进程未运行于独立 docker 实例中时)使用 jps 命令

手册页特别点出 docker 场景:如果目标 JVM 运行在单独的容器里,宿主机上的jps列不到它,需要在目标容器内执行。

选项一览

手册页给出了如下选项。先说明一个总原则:如果不使用下列任何选项,jinfo会同时打印命令行标志和系统属性键值对(详见后文默认行为小节)。

选项作用
-flag name打印指定命令行标志的名称与值
-flag [+|-]name启用(+)或禁用(-)指定的布尔型命令行标志
-flag name=value将指定的命令行标志设置为给定值
-flags打印传递给 JVM 的全部命令行标志
-sysprops以键值对形式打印 Java 系统属性
-h-help打印帮助信息

从源码看,这几个选项与手册页一一对应:JInfo.java 的参数解析循环只识别-flag-flags-sysprops三类功能选项,以及-?-h--help-help(其中-help被标注为 “legacy” 遗留别名)四类帮助选项。值得注意的是,帮助选项在源码中比手册页多认一个--help形式。

三个-flag变体在实现中收敛为对同一个flag()方法的不同调用,其分派逻辑是(JInfo.java):

int index = option.indexOf('='); if (index != -1) { flag = option.substring(0, index); String value = option.substring(index + 1); in = vm.setFlag(flag, value); // 形式三:-flag name=value } else { char c = option.charAt(0); switch (c) { case '+': in = vm.setFlag(flag, "1"); break; // 形式二:+name,布尔置真 case '-': in = vm.setFlag(flag, "0"); break; // 形式二:-name,布尔置假 default: in = vm.printFlag(flag); break; // 形式一:只读打印 } }

也就是说,-flag +name/-flag -name在底层等价于setFlag(name, "1"/"0")-flag name则走只读通道printFlag(name)

无选项时的默认行为:手册页之外的第三类输出

手册页说“不使用任何选项时,同时打印命令行标志与系统属性键值对”。而源码里的默认分支还多做了一件事(JInfo.java):

if (!doFlag && !doFlags && !doSysprops) { sysprops(pid); // Java System Properties flags(pid); // VM Flags commandLine(pid); // VM.command_line —— 额外打印目标 JVM 的启动命令行 }

因此裸执行jinfo <pid>时实际输出三段:Java System Properties:VM Flags:,以及紧随其后的目标 JVM 原始启动命令行(通过VM.command_line诊断命令获得)。这一“附赠”输出在排查“这个进程到底是用什么参数启动的”时非常实用。

平台限制与前置条件

手册页 Description 节列出的三条限制,是使用前必须核对的环境事实:

  1. Windows 依赖调试器支持:在缺少dbgeng.dll的 Windows 系统上,必须安装Debugging Tools for Windows这些工具才能工作;
  2. PATH需包含jvm.dllPATH环境变量应包含目标进程所使用的jvm.dll的位置,或包含 core dump 文件来源的位置;
  3. 临时目录必须一致(-XX:AltTempDir:如果目标 JVM 是以非默认临时目录启动的,jinfo必须使用同一个临时目录才能与之通信;默认情况下二者一致,所以不受影响。

从源码结构看,第 3 条的原理在于 Attach 通信依赖目标 JVM 在临时目录下建立的标识文件。ProcessArgumentMatcher.java 的注释明确提到了这一机制:

// If this is the case, then the /tmp/hsperfdata_xxx/pid file // will have disappeared and we will get a NullPointerException.

即工具通过/tmp/hsperfdata_<用户>/下的pid文件识别 Java 进程(这正是hsperfdata机制)。若目标进程用-XX:AltTempDir把该目录挪到了别处,工具在默认目录下既找不到进程标识、也无法送达 Attach 请求,这就是手册页强调“必须使用相同临时目录”的根因。

实操示例

以下示例假定本机有一个正在运行的 Java 进程,PID 为<pid>(可用jpsps获取)。

打印全部配置(默认行为,三段输出)

jinfo <pid>

查看单个标志

jinfo -flag MaxHeapSize <pid> # 典型输出:MaxHeapSize=<字节数>

查看全部 JVM 标志 / 全部系统属性

jinfo -flags <pid> # 输出以 "VM Flags:" 开头 jinfo -sysprops <pid> # 输出以 "Java System Properties:" 开头

动态修改运行中的进程

jinfo -flag MaxHeapSize=2g <pid> # 将数值型标志设为新值 jinfo -flag +PrintGCDetails <pid> # 启用布尔标志 jinfo -flag -PrintGCDetails <pid> # 禁用布尔标志

关于动态修改的边界,HotSpot 服务端会校验标志的可写性。attachListener.cpp 中set_flag的实现调用WriteableFlags::set_flag(..., JVMFlagOrigin::ATTACH_ON_DEMAND, ...),若标志被标记为不可写,返回JVMFlag::NON_WRITABLE,工具侧即收到flag '%s' cannot be changed的报错;标志不存在时print_flag则返回no such flag '%s'(attachListener.cpp)。换句话说:能否jinfo -flag修改某个参数,取决于该参数在 HotSpot 中的JVM_FLAG声明是否带可写属性,而非jinfo本身的行为。

帮助

jinfo -h

帮助文本由 JInfo.java 的usage()打印,除手册页所列选项外还展示了用法形式jinfo <option> <pid> (to connect to a running process)

源码实现原理

入口与参数解析

jinfo的入口是 JInfo.java 的main方法,流程为:

  1. 无参数→ 直接打印 usage 并以退出码 1 结束;

  2. checkForUnsupportedOptions(L210-L237):专门检测旧版 SA(Serviceability Agent)用法。出现-F选项,或-flag之外的非选项参数超过 1 个时,报错退出并提示:

    Cannot connect to core dump or remote debug server. Use jhsdb jinfo instead

    这是手册页没有明说、但对排查老脚本很有用的信息:早期jinfo可以附加到 core dump 或远程调试服务器(SA 模式),该能力已从jinfo移除,迁移到了jhsdb jinfo

  3. 选项解析:循环消费以-开头的参数,识别功能选项与帮助选项;

  4. PID 校验:选项之后必须恰好剩下 1 个非选项参数(L95-L100),否则 usage 退出;

  5. 进程匹配与执行:将 PID 传给ProcessArgumentMatcher解析,随后按选项分派到sysprops/flags/commandLine/flag

进程匹配:不只是裸 PID

ProcessArgumentMatcher(源码)对参数做二分处理:

  • 参数能解析为非零整数→ 视为精确 PID(singlePid),此时甚至跳过 AttachProvider 的进程列表直接返回该 PID——源码注释说明这是因为“当 VM 处于 debuggee-suspended 状态时 AttachProvider 不会列出它”(L151-L159);
  • 否则视为主类名的模糊匹配串:遍历VirtualMachine.list()列出的所有 Java 进程,用ProcessHelper.getMainClass(平台相关实现)或回退到jvmstatMonitoredVmUtil.mainClass取得各进程主类,做子串匹配,并排除jinfo工具自身。匹配到多个进程时,main会为每个 PID 的输出加一行Pid:<pid>前缀(JInfo.java);一个都没匹配到时打印Could not find any processes matching : '...'并退出码 1。

匹配阶段的失败路径也值得注意:注释中解释了目标 JVM 在查询途中退出时hsperfdata文件消失会触发 NPE,被特意捕获为“正常不匹配”(ProcessArgumentMatcher.java),这是对工具与目标进程生命周期竞态的显式处理。

Attach 通道:jinfo 与 jcmd 共用同一底层

jinfo的每个功能最终都落在 HotSpotVirtualMachine 的三类方法上:

jinfo 功能调用发往目标 VM 的命令
-flag name打印vm.printFlag(name)(L309-L311)printflag <name>
-flag name=value/+/-vm.setFlag(name, value)(L304-L306)setflag <name> <value>
-flagsvm.executeJCmd("VM.flags")jcmd VM.flags
-syspropsvm.executeJCmd("VM.system_properties")jcmd VM.system_properties
默认行为附带的命令行vm.executeJCmd("VM.command_line")jcmd VM.command_line

executeJCmd的实现就是把参数包成executeCommand("jcmd", command)(L313-L315)——这正是“jinfo 的查询能力可以用 jcmd 等价替代”的源码级依据。

输出读取由 JInfo.java 的drain()完成:以 UTF-8 把目标 VM 的响应流抽干打印到 stdout,随后vm.detach()断开连接。两个可配置的行为开关来自 HotSpotVirtualMachine 的静态初始化:

  • jdk.attach.allowAttachSelf(默认允许):是否允许附加到自身进程;自附加时流式输出会被强制禁用(L416-L422);
  • jdk.attach.allowStreamingOutput(默认允许):是否启用流式输出,影响大输出场景(如海量标志)的读取方式;
  • 附加超时sun.tools.attach.attachTimeout(毫秒),默认 10000ms(L606-L633),目标进程长期无响应时可据此调整等待。

此外,工具端在建立通信前会先发送getversion探测目标 VM 支持的 Attach 协议版本(v1/v2)与streaming选项(L371-L403);版本不匹配时目标端返回固定错误码,工具端会抛出Protocol mismatch with target VM(L481-L511)——用旧 JDK 的jinfo操作新 JVM(或反之)出现该报错时,原因即在于此。

HotSpot 服务端:命令分派表

目标 JVM 一侧,Attach Listener 线程维护一张命令分派表(attachListener.cpp):

static AttachOperationFunctionInfo funcs[] = { { "agentProperties", get_agent_properties }, { "datadump", data_dump }, { "dumpheap", dump_heap }, { "load", load_agent }, { "properties", get_system_properties }, { "threaddump", thread_dump }, { "inspectheap", heap_inspection }, { "setflag", set_flag }, { "printflag", print_flag }, { "jcmd", jcmd }, { "getversion", get_version }, { nullptr, nullptr } };

setflag/printflag两个jinfo专用入口与jcmd入口并列存在,说明jinfo并非独立机制,而是 Attach Listener 命令集的一个消费方。print_flag通过JVMFlag::find_flag(name)在全局标志表中查找并以print_as_flag格式化输出;set_flag则要求标志可写,来源标记为JVMFlagOrigin::ATTACH_ON_DEMAND(即“attach 时按需设置”),这一来源标记会被 HotSpot 记录在标志的 origin 中,后续用-flag读回时可以看到该标志是被 attach 修改过的。

边界行为与常见报错对照

把手册页与源码结合起来,可以整理出以下可预期的行为边界,便于排错时快速定位:

现象原因(源码依据)
Usage: jinfo <option> <pid> ...且退出码 1无参数、选项后缺少参数、或选项外参数多于 1 个(JInfo.java)
Could not find any processes matching : '...'PID 不是 Java 进程,或类名模糊匹配失败(L105-L108)
Error: -F option used/Cannot connect to core dump or remote debug server. Use jhsdb jinfo instead使用了已移除的 SA 模式选项(L210-L237)
<pid>: <attach 失败信息>VirtualMachine.attach(pid)抛异常,逐字打印消息后退出(attach 方法),常见于权限不足、跨用户、容器隔离、或AltTempDir不一致
flag '%s' cannot be changed目标标志不可写(attachListener.cpp)
no such flag '%s'标志名拼写错误或该版本不存在此标志
Protocol mismatch with target VM工具与目标 JVM 的 Attach 协议版本不兼容(HotSpotVirtualMachine.java)

相关工具与延伸

jinfo与同目录手册页中的 jcmd、jps、jmap、jstack、jstat 同属 JDK 诊断命令族,共享同一套 Attach 基础设施。结合本文的源码分析,可以给出三条实用选型结论:

  • 列进程jps(或ps),配合jinfo <pid>使用;
  • 查询配置jinfo -flags/-syspropsjcmd <pid> VM.flags/VM.system_properties等价,后者属于受支持的诊断命令接口;
  • 动态改标志jinfo -flagjcmd <pid> VM.set_flag <name>=<value>走同一条setflag通道,可写性约束完全一致。

最后重申适用前提:jinfo定位为实验性、不受支持的命令,其可用性与形态可能随 JDK 版本演进变化;涉及对生产系统的长期诊断与自动化,建议以jcmd/jhsdb为主、jinfo为辅助对照手段。

【免费下载链接】jdkJDK main-line development https://openjdk.org/projects/jdk项目地址: https://gitcode.com/GitHub_Trending/jd/jdk

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 12:56:40

K-means聚类算法原理与Python实现详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 12:53:05

单机无穷大系统仿真:从Simulink建模到暂态稳定分析

简介&#xff1a;单机无穷大系统是电力系统暂态稳定性分析的经典简化模型&#xff0c;这份MATLAB脚本仿真代码面向电力系统专业学生、研究人员及工程技术人员&#xff0c;用于研究一台发电机经无穷大母线接入电网时的功率振荡与稳定恢复特性。压缩包内仅含1个m文件&#xff0c;…

作者头像 李华
网站建设 2026/9/13 12:52:43

泰克示波器OpenChoice通信原理与VISA驱动深度排错指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 12:51:48

Gopeed 本地开发如何启动 API 后端与 Flutter 前端进行联调?

Gopeed 本地开发如何启动 API 后端与 Flutter 前端进行联调&#xff1f; 【免费下载链接】gopeed A fast, modern download manager for HTTP, BitTorrent, Magnet, and ed2k. Cross-platform, built with Golang and Flutter. 项目地址: https://gitcode.com/GitHub_Trendi…

作者头像 李华
网站建设 2026/9/13 12:49:30

Next.js + LangChain.js 构建前端可控AI Agent实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华