bigywater.tech
Java · 217

Java Compiler API:在程序里编译源代码

JDK 自带的编译器可以在程序里直接调用:源码字符串进、class 字节出。这篇真实运行内存编译与加载、诊断信息、警告选项、--release 的限制,以及运行时里缺少 jdk.compiler 时是什么表现。

150 → 8 ms
第一次编译 与 预热后的编译耗时
52 / 61 / 65
--release 8 / 17 / 21 的 class 主版本号
null
只有 java.base + java.compiler 的运行时里 getSystemJavaCompiler() 的返回

内存里编译、加载、调用

实测 · 本机执行输出
  第一次编译:成功 = true,耗时 150 ms,诊断 0 条
  内存里得到的 class:[demo.Hello$Inner, demo.Hello],字节数 [269, 381]
  用自定义 ClassLoader 加载并调用 applyAsInt(20) = 41,c.getClassLoader() 是 Main 的类加载器吗 = false,名字 = demo.Hello
  之后连续编译同一段代码 10 次的耗时(ms):[19, 15, 13, 12, 11, 10, 10, 10, 10, 8]

诊断信息、-Xlint 与 -Werror

实测 · 本机执行输出
  编译有错的代码:成功 = false,class 产出 0 个,诊断 3 条
    ERROR 第 3 行第 13 列 code=compiler.err.prob.found.req:incompatible types: java.lang.String cannot be converted to int
    ERROR 第 4 行第 5 列 code=compiler.err.cant.resolve.location.args:cannot find symbol
    ERROR 第 5 行第 16 列 code=compiler.err.cant.resolve.location:cannot find symbol
  默认选项编译带原始类型的代码:诊断 [NOTE:compiler.note.unchecked.filename, NOTE:compiler.note.unchecked.recompile]
  加 -Xlint:all:诊断 [WARNING:compiler.warn.raw.class.use, WARNING:compiler.warn.raw.class.use, MANDATORY_WARNING:compiler.warn.unchecked.call.mbr.of.raw.type]
  再加 -Werror:成功 = false,诊断 [WARNING:compiler.warn.raw.class.use, WARNING:compiler.warn.raw.class.use, MANDATORY_WARNING:compiler.warn.unchecked.call.mbr.of.raw.type, ERROR:compiler.err.warnings.and.werror]

--release 与 API 限制

实测 · 本机执行输出
  --release 8    成功 = true,class 文件主版本号 = 52(Java 8)
  --release 17   成功 = true,class 文件主版本号 = 61(Java 17)
  --release 21   成功 = true,class 文件主版本号 = 65(Java 21)
  --release 11 编译 record:成功 = false,[records are not supported in -source 11]
  --release 21 引用 sun.misc.Unsafe 构造器:成功 = false,[ERROR:Unsafe() has private access in sun.misc.Unsafe]
  --release 11 使用 List.getFirst()(Java 21 才有的 API):成功 = false,错误:cannot find symbol
  --release 17 使用 List.getFirst()(Java 21 才有的 API):成功 = false,错误:cannot find symbol
  --release 21 使用 List.getFirst()(Java 21 才有的 API):成功 = true
warning: [options] source value 8 is obsolete and will be removed in a future release
warning: [options] target value 8 is obsolete and will be removed in a future release
warning: [options] To suppress warnings about obsolete options, use -Xlint:-options.
3 warnings

运行时里没有编译器

实测 · 本机执行输出
  用完整 JDK 编译 Main:rc=0 
  jlink --add-modules java.base(运行时 47 MB):rc=1  Exception in thread "main" java.lang.NoClassDefFoundError: javax/tools/ToolProvider; at Main.main(Main.java:1)
  jlink --add-modules java.base,java.compiler(运行时 48 MB):rc=0  ToolProvider.getSystemJavaCompiler() = null
  jlink --add-modules java.base,java.compiler,jdk.compiler(运行时 67 MB):rc=0  ToolProvider.getSystemJavaCompiler() = com.sun.tools.javac.api.JavacTool@ba4d54
  完整 JDK 21 目录大小 350 MB

分步演示

Java Compiler API
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12 自带 javac;jlink 生成的运行时都是本机现做的
    • 没有验证:annotation processor 在内存编译里的行为、多个源文件之间的依赖、classpath 上的第三方库、编译器的并发使用、模块化代码(module-info)的编译、增量编译与缓存;耗时数字只代表这台机器上的少量运行
    京ICP备2026067502号-1京公网安备11010502063487号