jlink 可以把 JDK 裁剪成只含应用所需模块的运行时。jdeps 能找出需要的模块吗?各个选项能省多少体积?裁剪之后启动时间会变吗?漏掉一个反射依赖会怎样?这篇用一个真实应用逐项测量。
jdeps --print-module-deps app.jar → java.base,java.logging,java.net.http jdeps -s(摘要): app.jar -> java.base | app.jar -> java.logging | app.jar -> java.net.http 完整 JDK 21 的模块数:69;目录大小 350 MB
java.base 只有 base 47.3 MB,jlink 耗时 0.5 s jdeps 给出的模块 49.3 MB,jlink 耗时 0.5 s jdeps 的模块 + --strip-debug 44.5 MB,jlink 耗时 1.0 s + --compress=zip-6 30.0 MB,jlink 耗时 1.6 s + --compress=zip-9 30.0 MB,jlink 耗时 1.7 s 不加任何选项 49.8 MB,jlink 耗时 0.5 s zip-9 + --generate-cds-archive 56.6 MB,jlink 耗时 3.1 s 对照:完整 JDK 21 350 MB;jdeps 的模块 + strip-debug + zip-9 是它的 9% 带 --generate-cds-archive 的镜像里的 CDS 归档文件: ['classes_nocoops.jsa 13.5 MB', 'classes.jsa 13.2 MB'] 最小镜像(zip-9)的 java --list-modules: ['java.base@21.0.12.1', 'java.logging@21.0.12.1', 'java.net.http@21.0.12.1']
完整 JDK 21 Hello 启动中位 24 ms;输出 hello
jlink 镜像(jdeps 模块,zip-9) Hello 启动中位 40 ms;输出 hello
jlink 镜像(zip-9 + CDS 归档) Hello 启动中位 24 ms;输出 hello
jlink 镜像(只有 java.base) Hello 启动中位 36 ms;输出 hello
只有 java.base 的镜像运行 Main(用到 java.logging 与 java.net.http): Exception in thread "main" java.lang.NoClassDefFoundError: java/util/logging/Logger … ... 1 more
jdeps 模块的镜像运行 Main: OK | Oct 03, 2026 7:06:21 PM demo.Main main | INFO: HttpClient 创建成功:HTTP_2
Main 通过反射 Class.forName("java.sql.DriverManager")(jdeps 看不到的依赖,镜像里没有 java.sql): Exception in thread "main" java.lang.ClassNotFoundException: java.sql.DriverManager
同一个反射在完整 JDK 里: Oct 03, 2026 7:06:24 PM demo.Main main | INFO: HttpClient 创建成功:HTTP_2java.base 不带 --bind-services:47.3 MB,模块 1 个;带 --bind-services:163.1 MB,模块 45 个