一、概述
JDK(Java Development Kit)不只是编译器和运行时,它附带了一整套工程化工具。以本机 JDK 17.0.12 为例,bin 目录下共 35 个可执行文件,按功能可分为六大类:
| 分类 | 工具数量 | 核心用途 |
|---|---|---|
| 编译与运行 | 3 | 把源码编译成字节码、启动 JVM 执行 |
| 类与字节码 | 9 | 反汇编、依赖分析、模块化打包、签名 |
| JVM 监控与诊断 | 11 | 线上问题排查、性能分析、堆/线程快照 |
| 安全与证书 | 4 | 密钥管理、Kerberos 认证 |
| 文档与开发辅助 | 6 | API 文档、调试、脚本、交互式编程 |
| RMI 与序列化 | 2 | 远程方法调用、序列化版本号 |
| 辅助工具 | 2 | 无障碍辅助 |
二、编译与运行工具
2.1 javac:Java 编译器
功能:把 .java 源码编译成 .class 字节码。
# 基本用法
javac Hello.java # 编译单个文件,输出 Hello.class
javac -d out src/*.java # 编译 src 下所有 java,结构化输出到 out/
javac -cp lib/*:src -d out src/*.java # 指定 classpath(依赖的 jar 或其他类)
javac -encoding UTF-8 Hello.java # 指定源码编码(中文环境常见问题)
javac --release 8 Hello.java # 交叉编译,生成兼容 Java 8 的字节码
javac -Xlint:all Hello.java # 开启所有编译器警告
javac -g Hello.java # 生成带调试信息的 class(行号、局部变量名)
javac -proc:none Hello.java # 关闭注解处理
常用参数速查:
| 参数 | 作用 |
|---|---|
-d <目录> | 指定 class 输出目录,按包结构建子目录 |
-cp/-classpath | 指定编译时依赖的类路径 |
--release <N> | 兼容目标版本的 API(比 -source/-target 更安全) |
-encoding | 源码文件编码,Windows 下默认 GBK,通常指定 UTF-8 |
-Xlint:all | 开启全部警告(unchecked/deprecation/rawtypes 等) |
-g | 生成完整调试信息,配合 jdb 断点调试 |
-parameters | 在 class 中保留方法参数名(反射/框架能拿到参数名) |
-processor | 指定注解处理器 |
典型排错:
# 中文注释报乱码错误
javac -encoding UTF-8 Hello.java
# 找不到依赖类(比如 Guava)
javac -cp lib/guava-31.1.jar App.java
# 代码里用了 Java 11 的 API,但要兼容 Java 8
javac --release 8 App.java # 编译期就会报:String.isBlank() 在 Java 8 不存在
2.2 java:启动 JVM 执行
功能:启动 Java 虚拟机,加载主类执行 main 方法。
# 基本用法
java Hello # 执行当前目录的 Hello.class(不要写 .class 后缀)
java -cp out com.example.Hello # 指定类路径 + 全限定类名
java -jar app.jar # 执行可执行 jar(MANIFEST.MF 里指定了 Main-Class)
java -Xmx2g -Xms512m -jar app.jar # 设置堆内存:最大 2G,初始 512M
# 常用 JVM 参数
java -XX:+PrintGCDetails -jar app.jar # 打印 GC 详情日志
java -XX:+HeapDumpOnOutOfMemoryError -jar app.jar # OOM 时自动 dump 堆
java -Dfile.encoding=UTF-8 -jar app.jar # 设置系统属性
java -javaagent:agent.jar -jar app.jar # 挂载 Java Agent
JVM 内存参数(高频面试/实战):
| 参数 | 作用 | 建议值 |
|---|---|---|
-Xms<size> | 堆初始大小 | 生产环境通常设成和 -Xmx 一样,避免堆动态扩容 |
-Xmx<size> | 堆最大大小 | 物理内存的 60%-70%,留足给元空间+堆外+OS |
-Xmn<size> | 新生代大小 | 默认约堆的 1/3,G1 收集器不建议手动设 |
-XX:MetaspaceSize | 元空间初始大小 | Java 8+ 用元空间替代永久代 |
-XX:MaxMetaspaceSize | 元空间最大大小 | 默认无上限,建议设置防止内存泄漏 |
-Xss<size> | 每个线程栈大小 | 默认 1M,递归很深的场景可以调大 |
-XX:+UseG1GC | 使用 G1 垃圾收集器 | Java 9+ 默认,大堆(>4G)推荐 |
-XX:+UseZGC | 使用 ZGC | Java 15+,超低停顿(<10ms),大堆首选 |
2.3 javaw:无控制台启动
功能:和 java 完全一样,区别是不会弹出/占用命令行窗口。
适用场景:桌面 GUI 程序(Swing/JavaFX)、Windows 后台服务。
# 启动 Swing GUI 程序,不阻塞命令行
javaw -jar my-gui-app.jar
三、类与字节码工具
3.1 javap:字节码反汇编(必学)
功能:把 .class 字节码反汇编成人类可读的 JVM 指令,是理解 Java 编译器行为、排查语法糖真相的第一工具。
javap Hello # 打印类的 public 成员签名
javap -p Hello # 打印所有成员(含 private)
javap -c Hello # 打印字节码指令(重点学习)
javap -c -l -v Hello # -l 行号表+局部变量表,-v 完整附加信息
javap -c java.lang.String # 反汇编 JDK 自带的类
实战示例:看 i++ 和 ++i 到底差在哪
public class Incr {
public static void main(String[] args) {
int a = 0;
int b = a++;
int c = ++a;
}
}
javac Incr.java && javap -c Incr
输出(节选):
Code:
stack=1, locals=4, args_size=1
0: iconst_0 # 把 0 压栈
1: istore_1 # 弹出存到 a → a=0
2: iload_1 # a 的值(0) 压栈
3: iinc 1, 1 # a 自增 1 → a=1
6: istore_2 # 弹出 0 给 b → b=0
7: iinc 1, 1 # a 自增 1 → a=2
10: iload_1 # a 的值(2) 压栈
11: istore_3 # 弹出给 c → c=2
12: return
结论:b=a++ 是"先取原值,再自增";c=++a 是"先自增,再取新值"。字节码层看得一清二楚,不用死记。
javap 能解决的常见问题:
- 自动装箱/拆箱发生在哪里
- 字符串拼接是用 StringBuilder 还是 invokedynamic
- 泛型桥接方法是否存在
- for-each 循环编译后是 iterator 还是数组遍历
- try-with-resources 展开成了什么样
- lambda 表达式编译成了什么(invokedynamic + 内部类)
3.2 jar:打包/解包 JAR 文件
功能:JAR 本质是 ZIP 格式,jar 命令用来创建、查看、解压 JAR 文件。
# 创建 JAR
jar cf app.jar -C out . # 把 out 目录下所有文件打包成 app.jar
jar cfe app.jar com.example.Main -C out . # 可执行 JAR:指定 Main-Class 入口
jar cmf MANIFEST.MF app.jar -C out . # 自定义 MANIFEST.MF
# 查看 JAR
jar tf app.jar # 列出 JAR 内所有文件
jar tvf app.jar # 带大小/日期/所有者的详细列表
# 解压 JAR
jar xf app.jar # 解压到当前目录
jar xf app.jar com/example/A.class # 只解压某个文件
# 更新 JAR
jar uf app.jar new.properties # 往已有的 JAR 里加文件
可执行 JAR 的关键:MANIFEST.MF 里必须有 Main-Class: com.example.Main,且最后要空一行。
3.3 jarsigner:JAR 签名与验证
功能:用私钥给 JAR 数字签名,接收方用公钥验签,确保 JAR 没有被篡改、来源可信。
# 签名(先用 keytool 生成密钥对)
jarsigner -keystore my.keystore app.jar myalias
jarsigner -sigalg SHA256withRSA -digestalg SHA-256 app.jar myalias
# 验签
jarsigner -verify app.jar
jarsigner -verify -verbose -certs app.jar # 详细验证+证书链展示
应用场景:Java Web Start(JNLP)、Applet(已废弃)、需要来源可信的分发场景。
3.4 jdeps:类与 JAR 依赖分析
功能:静态分析 class/JAR 的依赖关系,找出依赖了哪些包、哪些 JDK 内部 API(升级 JDK 必备)。
# 分析单个 JAR 的依赖
jdeps app.jar
jdeps -v app.jar # 详细输出(类级别的依赖)
# 分析依赖的 JDK 模块(JDK 9+ 模块化)
jdeps --list-deps app.jar # 列出依赖了哪些 JDK 模块(java.base 等)
jdeps --print-module-deps app.jar # 只输出模块名,可直接喂给 jlink
# 找 JDK 内部 API 调用(JDK 8 升级 JDK 11/17 的第一道关)
jdeps --jdk-internals app.jar # 标出 sun.misc.* 等内部 API
jdeps --internal-apionly app.jar # 只看内部 API
# 递归分析依赖的 JAR
jdeps -cp 'lib/*' --recursive app.jar
典型场景:从 JDK 8 升级 JDK 17 时,先跑 jdeps --jdk-internals app.jar,所有用了 sun.misc.BASE64Encoder 等内部 API 的类直接标出来,省得运行时抛异常。
3.5 jdeprscan:扫描废弃 API
功能:专门扫描 class/JAR 里调用了哪些 @Deprecated 的方法/类,包括 JDK 废弃的和自定义废弃的。
# 扫描废弃的 JDK API 调用
jdeprscan app.jar
jdeprscan --release 17 app.jar # 按 JDK 17 的废弃列表来扫
jdeprscan --release 11 app.jar # 按 JDK 11 的废弃列表
jdeprscan --verbose app.jar # 详细:每个调用点的位置
和 jdeps 的区别:jdeps 关注"依赖了什么",jdeprscan 只关注"用了哪些被标记为 deprecated 的 API"。升级 JDK 前两个工具配合使用。
3.6 jmod:JMOD 模块化打包
功能:JDK 9 引入的模块化格式(.jmod),比 JAR 能装更多东西:本地代码(.so/.dll)、配置文件、命令、头文件等。
# 创建 jmod
jmod create --class-path classes --libs lib/ --cmds bin/ mymodule.jmod
# 查看 jmod 内容
jmod list mymodule.jmod
# 描述模块信息
jmod describe mymodule.jmod
# 提取 jmod 内容
jmod extract --dir out mymodule.jmod
# 计算哈希(放到 module-info.java 的 hashes 里,防篡改)
jmod hash --module-path mods --output hashes.txt mymodule.jmod
JDK 自带的模块就打包成 .jmod 放在 $JAVA_HOME/jmods/ 目录里。
3.7 jimage:JIMAGE 运行时镜像操作
功能:JDK 9+ 用 JIMAGE(.jimage)格式把 JDK 内置模块打包在 lib/modules 文件里,比 JAR 体积更小、加载更快。jimage 用来查看/提取它的内容。
# 列出 JIMAGE 里的资源
jimage list $JAVA_HOME/lib/modules
# 提取某个资源(比如 java/lang/String.class)
jimage extract --dir out $JAVA_HOME/lib/modules java/lang/String.class
# 查看 JIMAGE 的基本信息
jimage info $JAVA_HOME/lib/modules
jimage verify $JAVA_HOME/lib/modules # 校验一致性
平时开发一般不用,排查 JDK 自身的类加载问题或做定制 JRE 时会用到。
3.8 jlink:裁剪 JDK,生成定制运行时(重要)
功能:JDK 9+ 的杀手锏工具。只把应用实际用到的 JDK 模块打包成一个最小 JRE,不再需要分发 200MB+ 的完整 JDK。
# 第一步:用 jdeps 拿到依赖的模块列表
jdeps --print-module-deps app.jar
# 输出:java.base,java.logging,java.xml
# 第二步:jlink 生成最小运行时
jlink --module-path $JAVA_HOME/jmods \
--add-modules java.base,java.logging,java.xml \
--output my-mini-jre \
--compress 2 \
--strip-debug \
--no-header-files \
--no-man-pages
# 第三步:发布时把 app.jar + my-mini-jre 一起打包即可
my-mini-jre/bin/java -jar app.jar
裁剪效果:完整 JDK ~300MB,最小 java.base JRE ~40MB,差异巨大。Docker 镜像瘦身的必备工具。
常用 jlink 优化参数:
| 参数 | 作用 |
|---|---|
--compress 2 | ZIP 级别压缩,压得更狠 |
--strip-debug | 去掉调试符号 |
--strip-native-commands | 去掉 jmap/jstack 等诊断命令(生产可不需要) |
--no-header-files / --no-man-pages | 去掉头文件和 man 手册 |
--vm=server/client/minimal | 选择 JVM 模式(server 默认) |
3.9 jpackage:打包成操作系统原生安装包
功能:JDK 14+ 正式加入。把 JAR + 定制 JRE 打包成 Windows .msi/.exe、macOS .dmg/.pkg、Linux .deb/.rpm,带桌面图标、开始菜单、卸载入口。
# Windows 打包成 exe 安装程序
jpackage --type exe \
--name MyApp \
--input lib/ \
--main-jar app.jar \
--main-class com.example.Main \
--icon app.ico \
--win-menu \
--win-dir-chooser \
--win-shortcut
# 不打包 JRE(假定目标机已有 JRE)
jpackage --type exe --name MyApp --input lib/ --main-jar app.jar --app-version 1.0
JDK 17 对 jpackage 做了大量修复和增强,是 Java 桌面应用分发的官方推荐方式。
四、JVM 监控与诊断工具(线上排障核心)
这 11 个工具是线上问题排查的"瑞士军刀",按使用频率排序。
4.1 jps:查看 Java 进程(必用第一步)
功能:列出当前机器所有正在运行的 Java 进程和它们的 PID。Windows 任务管理器 / Linux ps 看 Java 进程不如 jps 直接。
jps # 输出:PID + 主类名/JAR 名
jps -l # 输出:PID + 主类全名
jps -v # 输出:启动时传的 JVM 参数(看堆多大、用的什么 GC)
jps -m # 输出:传给 main 方法的参数
jps -lv # 常用组合:PID + 主类全名 + JVM 参数
典型输出:
12345 org.springframework.boot.loader.JarLauncher -Xmx2g -XX:+UseG1GC
23456 sun.tools.jps.Jps
做任何诊断前先跑 jps -lv,拿到目标进程 PID,后面所有工具都要传 PID。
4.2 jstat:JVM 运行时指标(高频快照)
功能:连续采样 JVM 的类加载、GC、JIT 编译等核心指标,是判断"内存有没有泄漏、GC 正常不正常"的第一工具。
# 最常用:GC 统计,每 1 秒采样一次,连续采 10 次
jstat -gc 12345 1000 10
# 输出字段解读(关键):
# S0C/S1C: Survivor 0/1 容量 (KB)
# S0U/S1U: Survivor 0/1 已用
# EC/EU: Eden 容量/已用
# OC/OU: Old 容量/已用
# MC/MU: Metaspace 容量/已用
# YGC/YGCT: Young GC 次数/总耗时
# FGC/FGCT: Full GC 次数/总耗时
# GCT: GC 总耗时
其他常用子选项:
jstat -gcutil 12345 1000 # 用百分比显示(已用/容量%),一眼看 Eden、Old 占比
jstat -gccause 12345 # 比 gcutil 多两列:上一次 GC 的原因
jstat -class 12345 # 类加载/卸载统计
jstat -compiler 12345 # JIT 编译统计
jstat -printcompilation 12345 # 最近被 JIT 编译的方法
诊断用法:
- 跑 jstat -gcutil 12345 5000 20,观察 OU(Old 已用)曲线。每次 Young GC 后 OU 不断上涨 → 怀疑内存泄漏
- FGCT(Full GC 总耗时)持续攀升 → 调 GC 参数或升级收集器
- YGC 每秒好几十次 → 新生代太小,调 -Xmn
4.3 jstack:线程快照 + 死锁检测
功能:dump 某 Java 进程中所有线程的调用栈,并自动检测死锁。
# dump 线程栈到控制台
jstack 12345
# 输出到文件
jstack 12345 > thread_dump.txt
# 强制 dump(进程卡住不响应时,-F)
jstack -F 12345
# 带锁信息(默认就有)
jstack -l 12345
看线程栈的三个关键动作:
- 找死锁:文件末尾有
Found one Java-level deadlock:段落,直接列出死锁的线程、各自持有什么锁、等什么锁 - 找 BLOCKED 线程:搜索
java.lang.Thread.State: BLOCKED,看它们卡在哪个synchronized代码块 - 找 CPU 高的线程:(Linux)先
top -H -p 12345找到耗 CPU 的线程 TID → 转十六进制 → 在 jstack 输出里搜对应nid=0x...的线程,看它在跑什么方法
典型死锁输出节选:
Found one Java-level deadlock:
=============================
"Thread-0":
waiting to lock monitor 0x... (which is held by "Thread-1")
"Thread-1":
waiting to lock monitor 0x... (which is held by "Thread-0")
Java stack information for the threads listed above:
===================================================
"Thread-0":
at DeadLock$1.run(DeadLock.java:15)
- locked <0x...> (a java.lang.Object) --> 持有锁 A
- waiting to lock <0x...> (a java.lang.Object) --> 等锁 B
4.4 jmap:堆内存快照(OOM 排查核心)
功能:查看堆信息、生成堆转储文件(heap dump)。
# 查看堆总体信息(各代容量、使用量、GC 收集器)
jmap -heap 12345
# 查看各代对象统计(按类聚合数量和大小)
jmap -histo 12345 # 所有对象
jmap -histo:live 12345 # 只统计活对象(会触发一次 Full GC)
# 生成堆转储文件(最常用!)
jmap -dump:format=b,file=heap.hprof 12345
jmap -dump:live,format=b,file=heap_live.hprof 12345 # 只 dump 活对象,文件更小
heap dump 怎么分析:
- 用 JDK 自带的 jvisualvm(JDK 8 有,JDK 9+ 需要单独装 VisualVM)
- 用 Eclipse MAT(Memory Analyzer Tool)——行业标准,自动出泄漏嫌疑报告
- 用 JDK 自带的 jhat(已废弃,不推荐),或在线工具:HeapHero
heap dump 文件 = 当时堆的完整快照——里面所有对象、引用关系、GC Roots 全在。内存泄漏 90% 的真相都在里面。
4.5 jinfo:查看/修改 JVM 参数
功能:查看 JVM 进程的系统属性和 JVM 标志位,并且可以运行时动态修改部分可管理标志(不用重启进程)。
# 查看所有系统属性(=System.getProperties() 的内容)
jinfo -sysprops 12345
# 查看所有 JVM 标志位
jinfo -flags 12345
# 查看单个标志位的值
jinfo -flag PrintGC 12345
# 运行时开关某个标志位(不用重启)
jinfo -flag +PrintGC 12345 # 开启 GC 日志
jinfo -flag -PrintGC 12345 # 关闭 GC 日志
jinfo -flag MaxHeapFreeRatio=70 12345 # 修改数值型参数(不是所有都能改)
# 把所有信息(属性+标志+命令行)汇总输出
jinfo 12345
哪些标志可以动态改?看 java -XX:+PrintFlagsFinal -version | grep manageable,标了 manageable 的才可以运行时改。
4.6 jcmd:瑞士军刀(统一命令)
功能:JDK 7u4 引入,统一替代 jps/jstack/jmap/jinfo 等大部分功能。语法统一,功能更强。
# 列出所有 Java 进程
jcmd
# 帮助:看某个进程支持哪些诊断命令
jcmd 12345 help
# 线程栈(= jstack)
jcmd 12345 Thread.print
jcmd 12345 Thread.print -l=true # 带锁信息
# 堆信息(= jmap -heap)
jcmd 12345 GC.heap_info
# 生成 heap dump(= jmap -dump)
jcmd 12345 GC.heap_dump heap.hprof
jcmd 12345 GC.heap_dump -all heap.hprof # 包含不可达对象
# 触发 GC
jcmd 12345 GC.run
jcmd 12345 GC.run_finalization
# 对象统计(= jmap -histo)
jcmd 12345 GC.class_histogram
jcmd 12345 GC.class_histogram -all=true
# 查看 JVM 标志(= jinfo)
jcmd 12345 VM.flags
jcmd 12345 VM.flags -all # 所有标志
# 查看系统属性
jcmd 12345 VM.system_properties
# JIT 编译统计
jcmd 12345 Compiler.codecache
jcmd 12345 Compiler.queue
# 动态开启/关闭 JFR(JDK Flight Recorder)
jcmd 12345 JFR.start name=myrec duration=60s filename=rec.jfr
jcmd 12345 JFR.check
jcmd 12345 JFR.stop name=myrec
趋势:官方推荐优先用 jcmd,jps/jstack/jmap/jinfo 这些单功能工具未来可能被淘汰。新环境上手直接 jcmd 即可。
4.7 jconsole:GUI 监控控制台
功能:JMX 图形化监控工具,实时看堆内存、线程、类加载、CPU 使用率、MBean 操作。
jconsole 12345 # 直接连本地 PID
jconsole host:port # 连远程主机(需开启 JMX 远程端口)
jconsole localhost:1099
打开后六个标签页:
- 概览:堆内存、线程、类、CPU 四张图联动
- 内存:各代(Eden/Survivor/Old/Metaspace)的占用曲线,可手动执行 GC
- 线程:线程列表、每个线程的栈、死锁检测按钮(一键检测)
- 类:已加载/卸载类的数量
- VM 摘要:JVM 参数、系统属性、JDK 版本汇总
- MBean:所有注册的 MBean,可以调用方法、读写属性(比如手动触发 GC、修改日志级别)
远程开启 JMX(生产要加鉴权+SSL):
java \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=1099 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-Djava.rmi.server.hostname=192.168.1.100 \
-jar app.jar
4.8 jfr:JDK Flight Recorder(生产级事件追踪)
功能:JDK 11 开源(Oracle JDK 7 就有但商业收费)。JVM 内建的超低开销事件采集系统,能记录几千种事件(GC、锁、线程、IO、异常、JIT 等),性能开销 <1%,生产环境可以常年开着。
# 方式一:启动时就开启 JFR
java -XX:StartFlightRecording:filename=rec.jfr,duration=1h -jar app.jar
# 方式二:运行时用 jcmd 开启
jcmd 12345 JFR.start name=myrec maxsize=500m maxage=24h
jcmd 12345 JFR.check
jcmd 12345 JFR.dump name=myrec filename=rec.jfr
jcmd 12345 JFR.stop name=myrec
# 方式三:jfr 工具(JDK 19+)
jfr start --pid 12345 --name myrec
jfr dump --name myrec --output rec.jfr
jfr summary rec.jfr # 打印事件摘要
jfr print rec.jfr # 打印全部事件(慎用,量大)
jfr print --events GC rec.jfr # 只看 GC 事件
JFR 文件怎么分析:
- 用 JDK Mission Control(JMC)——图形化分析器,火焰图、GC 详情、热点方法、锁竞争、异常统计全在里面
- JMC 需要单独下载(不再随 JDK 发布)
JFR + JMC 是目前 Java 生产诊断的黄金组合,比 Arthas/BTrace 更轻量、更全面,且 Oracle 官方出品。
4.9 jhsdb:HotSpot Serviceability Agent(深度调试)
功能:JDK 9+ 替代了旧的 jsadebugd,可以挂到运行中的 JVM 进程或核心转储(core dump),检查 JVM 内部状态(对象、栈、CodeCache、元空间)。
# 启动 GUI(类似旧的 HSDB)
jhsdb hsdb --pid 12345
# CLI 模式:连接进程/核心转储执行特定命令
jhsdb clhsdb --pid 12345
jhsdb clhsdb --exe java --core core.12345
# 常用子命令(连接后):
jhsdb jmap --pid 12345 --heap # 等于 jmap -heap
jhsdb jmap --pid 12345 --histo # 等于 jmap -histo
jhsdb jstack --pid 12345 # 等于 jstack
jhsdb jinfo --pid 12345 # 等于 jinfo
平时用 jcmd/jmap/jstack 够了,jhsdb 是 JVM 工程师或极端疑难杂症场景(比如 core dump 分析)才会用的重型工具。
4.10 jstatd:远程 jstat 守护进程
功能:在远端机器上启动一个 RMI 守护,本地 jvisualvm / jstat 可以远程监控。
# 简单启动(无安全策略,仅限内网)
jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=192.168.1.100
jstatd.all.policy 内容:
grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
生产环境不建议用(不安全),改 SSH 隧道 + 本地 jcmd 更稳妥。
五、安全与证书工具
5.1 keytool:密钥与证书管理(高频实用)
功能:JDK 自带的密钥库(keystore)和证书管理工具,用于 HTTPS、数字签名、JWT RSA 密钥对等场景。
# ===== 一、创建密钥 =====
# 生成 RSA 密钥对(2048 位),存进 JKS 密钥库
keytool -genkeypair \
-alias myserver \
-keyalg RSA -keysize 2048 \
-validity 3650 \
-keystore server.keystore \
-storepass changeit \
-dname "CN=localhost,OU=Dev,O=MyOrg,L=BJ,ST=BJ,C=CN"
# 生成证书签名请求(CSR),发给 CA 机构签名
keytool -certreq \
-alias myserver \
-file myserver.csr \
-keystore server.keystore
# 导入 CA 返回的证书
keytool -importcert -alias myserver -file myserver.crt -keystore server.keystore
# ===== 二、查看密钥库内容 =====
keytool -list -keystore server.keystore
keytool -list -v -keystore server.keystore -alias myserver # 看具体条目详情
keytool -list -rfc -keystore server.keystore # 以 RFC(PEM)格式输出
# ===== 三、JDK 信任库 =====
# JDK 自带的 CA 证书存放在:
# $JAVA_HOME/lib/security/cacerts(JDK 9+,旧版在 jre/lib/security/cacerts)
# 默认密码:changeit
# 查看 JDK 信任了哪些 CA 证书
keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit | wc -l
# 导入企业自签证书到 JDK 信任库(解决 HTTPS PKIX path building failed 错误)
keytool -importcert \
-alias corp-ca \
-file corp-ca.crt \
-keystore $JAVA_HOME/lib/security/cacerts \
-storepass changeit \
-noprompt
# 删除 JDK 信任库里的某个别名
keytool -delete -alias corp-ca -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit
# ===== 四、导出 =====
# 导出某个别名的证书(公钥)给对方
keytool -exportcert -alias myserver -file myserver.crt -keystore server.keystore
# 导出 PKCS12 格式(跨平台、兼容 OpenSSL),给 nginx/Apache 用
keytool -importkeystore \
-srckeystore server.keystore -srcstoretype JKS \
-destkeystore server.p12 -deststoretype PKCS12
两种常见密钥库格式:
| 格式 | 扩展名 | 说明 |
|---|---|---|
| JKS | .jks / .keystore | Java 原生格式,历史悠久,安全性较弱(仅密码保护) |
| PKCS12 | .p12 / .pfx | 行业标准,加密强度高,跨语言兼容,JDK 9+ 已经是默认格式 |
新建密钥库时建议直接用 PKCS12:
keytool -genkeypair -storetype PKCS12 ...
5.2 kinit / klist / ktab:Kerberos 认证三件套
功能:和 Hadoop、Kafka、企业 AD/LDAP 的 Kerberos SSO 认证交互。
# kinit:获取 TGT 票据(输入密码或用 keytab)
kinit admin@CORP.COM
kinit -kt admin.keytab admin@CORP.COM # 用 keytab 免密登录
# klist:查看当前缓存的票据
klist
klist -e # 看加密类型
# ktab:管理 keytab 文件(存密钥,免密登录用)
ktab -k admin.keytab -a admin@CORP.COM # 往 keytab 加条目
ktab -k admin.keytab -l # 列出 keytab 条目
ktab -k admin.keytab -d admin@CORP.COM # 删除条目
做大数据生态、接入企业 SSO 才会用到,普通 Java 开发者接触较少。
六、文档与开发辅助工具
6.1 javadoc:生成 API 文档
功能:从源码注释里的 /** ... */ Javadoc 标签,生成静态 HTML API 文档。
# 生成整个项目的文档
javadoc -d docs -sourcepath src -subpackages com.example
# 带更多选项
javadoc -d docs \
-sourcepath src \
-subpackages com.example \
-classpath lib/* \
-encoding UTF-8 \
-charset UTF-8 \
-docencoding UTF-8 \
-windowtitle "My Project API" \
-doctitle "My Project API Documentation" \
-author -version \
-link https://docs.oracle.com/en/java/javase/17/docs/api/ # 链接到 JDK 文档
常用 Javadoc 标签:
| 标签 | 作用 |
|---|---|
@param | 方法参数说明 |
@return | 返回值说明 |
@throws / @exception | 抛出的异常 |
@author | 作者(-author 参数才会显示) |
@version | 版本(-version 参数才会显示) |
@see / {@link} | 引用其他类/方法 |
@since | 从哪个版本开始加入 |
@deprecated | 废弃说明 |
6.2 jdb:命令行调试器
功能:JDK 自带的命令行 Java 调试器,类似 gdb/C++。没有 IDE 时的紧急调试手段。
# 启动被调试程序,开启 JDWP 调试端口
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -jar app.jar
# 另开窗口,jdb 连接上去
jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=5005
# 调试命令(和 gdb 风格类似)
stop in com.example.Main.main # 在方法入口打断点
stop at com.example.Main:20 # 在第 20 行打断点
run # 启动执行
cont # 继续运行
step # 单步进入(进入方法内部)
next # 单步跳过(不进入方法)
where # 打印当前线程栈
print varName # 打印变量值
locals # 打印当前所有局部变量
dump objName # 打印对象的所有字段
threads # 列出所有线程
thread 0x123 # 切换到某个线程
clear com.example.Main:20 # 清除断点
exit # 退出
现代开发基本都用 IDE 的调试器,jdb 是 Linux 无头环境紧急排错的兜底。
6.3 jshell:Java 交互式 REPL(JDK 9+)
功能:Java 的命令行交互式解释器,随手验证一段代码、试一个 API,不用写完整类、不用编译。
jshell
进入后的使用:
jshell> int a = 1 + 2
a ==> 3
jshell> System.out.println("Hello JShell")
Hello JShell
jshell> List<String> list = List.of("a", "b", "c")
list ==> [a, b, c]
jshell> list.stream().map(String::toUpperCase).toList()
$4 ==> [A, B, C]
jshell> /imports # 看已导入的包
jshell> /vars # 看定义的变量
jshell> /methods # 看定义的方法
jshell> /edit # 用外部编辑器打开当前会话代码
jshell> /save session.jsh # 保存会话到文件
jshell> /open session.jsh # 恢复保存的会话
jshell> /reset # 清空所有定义
jshell> /exit # 退出
非常适合:验证 Stream API、正则表达式、日期格式、数值转换这类一两行就能跑的小逻辑。
6.4 jrunscript:命令行脚本运行
功能:JDK 自带的脚本运行器,默认 JavaScript(Nashorn,JDK 11 后已被移除/废弃),但可以通过 -l 指定其他 JSR-223 兼容的脚本语言。
# 运行一段 JS(JDK 8 才有 Nashorn)
jrunscript -e "print('Hello')"
jrunscript script.js
# 列出可用的脚本引擎
jrunscript -q
JDK 17 里 Nashorn 已经移除,这个工具的实用价值下降。替代方案:直接用 Java 写 main 方法或 Groovy。
七、RMI 与序列化辅助
7.1 rmiregistry:RMI 注册表
功能:启动 RMI 命名服务的注册中心,服务端把远程对象 bind 上去,客户端通过名字 lookup 拿远程对象。
# 在端口 1099 启动(默认端口)
rmiregistry
# 指定其他端口
rmiregistry 2099
RMI(Remote Method Invocation)是 Java 原生的远程调用机制,Spring/Dubbo 生态普及后已经很少新用。遗留系统维护可能会碰到。
7.2 serialver:生成 serialVersionUID
功能:计算类的 serialVersionUID(序列化版本 UID)。
// 业务代码里经常这样写:
private static final long serialVersionUID = 1L;
但 1L 只是随便写的。真正的 UID 是根据类名、字段、方法签名等综合计算出来的哈希值。
serialver com.example.User # 输出计算出的 UID
serialver -classpath out com.example.User
# 示例输出:com.example.User: private static final long serialVersionUID = 6803793133171234567L;
直接把输出复制粘贴进代码,就是标准的 UID。比手动写 1L 更严谨(类结构变了 UID 会变,反序列化版本不一致直接报错,不会默默出错)。
八、辅助工具
| 工具 | 功能 |
|---|---|
jabswitch | Java Access Bridge:把 Java GUI 接入 Windows 无障碍 API,屏幕阅读器读 Swing 控件 |
jaccessinspector | 无障碍检查器:查看 Java GUI 组件的可访问性信息 |
jaccesswalker | 无障碍遍历器:层级浏览组件树,检查无障碍属性 |
这三个是 Java Accessibility 套件,纯辅助工具,和开发/运维几乎无关。
九、学习路径建议
| 阶段 | 重点掌握的工具 | 标准 |
|---|---|---|
| 第一阶段:开发日常 | javac、java、javadoc、jar、javap | 编译、打包、调试、看字节码不卡壳 |
| 第二阶段:问题排查 | jps、jstat、jstack、jmap、jconsole | 能独立排查 CPU 高、死锁、OOM、内存泄漏 |
| 第三阶段:进阶诊断 | jcmd、jinfo、jfr、jpackage、jlink | 能用 jcmd 统一操作、JFR 录制生产事件、裁剪 JRE 分发包 |
| 第四阶段:极端场景 | jhsdb、jdeps、jdeprscan、keytool | 能分析 core dump、升级 JDK 扫描兼容性、处理证书链问题 |
十、速查索引
| 目标 | 用什么工具 |
|---|---|
| 编译 Java 源码 | javac |
| 启动 Java 程序 | java(控制台)/ javaw(无控制台) |
| 打包解包 JAR | jar |
| 看字节码指令 | javap -c |
| 分析类/JAR 依赖 | jdeps |
| 找废弃 API 调用 | jdeprscan |
| 找 JDK 内部 API 调用 | jdeps --jdk-internals |
| 升级 JDK 前兼容性扫描 | jdeps + jdeprscan |
| 裁剪 JRE | jdeps --print-module-deps + jlink |
| 打包成 exe/msi 安装包 | jpackage |
| 找 Java 进程 PID | jps / jcmd |
| 看 GC/内存实时数据 | jstat -gcutil 1000 |
| 线程栈 + 死锁检测 | jstack / jcmd Thread.print |
| 堆 dump | jmap -dump / jcmd GC.heap_dump |
| 看堆各代使用量 | jmap -heap / jcmd GC.heap_info |
| 对象实例数排名 | jmap -histo:live / jcmd GC.class_histogram |
| 动态开关 GC 日志 | jinfo -flag +PrintGC |
| 统一诊断命令 | jcmd |
| GUI 监控 | jconsole / JMC(单独下载) |
| 生产级低开销事件追踪 | JFR(jcmd JFR.start + JMC 分析) |
| core dump 分析 | jhsdb clhsdb |
| 密钥库/证书管理 | keytool |
| Kerberos 认证 | kinit / klist / ktab |
| 生成 API 文档 | javadoc |
| 命令行调试器 | jdb |
| 交互式 Java | jshell |
| serialVersionUID 计算 | serialver |
十一、总结
JDK 的 bin 目录不是"编译器和 java 命令所在的文件夹",而是 Java 生态的一整套工具箱。
开发阶段:javac 编译、jar 打包、javadoc 生成文档、javap 看字节码查语法糖、jshell 快速验证小片段。
运行阶段:jps 找进程 → jstat 看 GC → jstack 看线程 → jmap 抓堆 → jcmd 做统一操作 → jconsole/JFR 做持续监控。按这个套路,线上 90% 的问题能定位。
分发阶段:jdeps 看依赖 → jlink 剪 JRE → jpackage 打 exe/msi,一套完成。
安全阶段:keytool 管密钥和证书、解决 HTTPS 自签证书报错。
一句话记忆:编译运行看 javac/java,线上排障走 jps-jstat-jstack-jmap-jcmd,分发打包 jlink+jpackage,证书密钥找 keytool。