JDK 8 到 24 全版本深度盘点:每个版本的功能、优点、坑与升级建议
java -version打出来的那个数字,决定了你能用什么语法、你的 GC 有多快、你的服务能扛多少并发。但绝大多数 Java 程序员的状态是:用着 JDK 8,听说 17 不错,知道虚拟线程很香,具体哪一版带来的、升级会踩什么坑,说不清。
这篇文章把 JDK 8 到 24(2026 年 8 月时点,25 已是最新 LTS)逐版本盘一遍:每个版本带来什么、好在哪、坑在哪、值不值得为它升级。信息以 Oracle/OpenJDK 官方发布说明和 Java Version Almanac 为准。先给一张全景图:
| 版本 | 发布 | 类型 | 一句话定位 |
|---|---|---|---|
| 8 | 2014.03 | LTS | 函数式革命,至今装机量最大 |
| 9 | 2017.09 | - | 模块化,争议最大的版本 |
| 10 | 2018.03 | - | var 局部变量推断 |
| 11 | 2018.09 | LTS | 首个"新节奏"LTS,HTTP Client |
| 12~16 | 2019~2021 | - | 渐进式语法铺垫(records/sealed 预览)+ ZGC/Shenandoah |
| 17 | 2021.09 | LTS | sealed + 模式匹配,生态迁移分水岭 |
| 18~20 | 2022~2023 | - | 虚拟线程孵化期 |
| 21 | 2023.09 | LTS | 虚拟线程正式版,近十年最大更新 |
| 22~24 | 2024~2025 | - | FFM 落地、Stream Gatherers、性能微调期 |
一、JDK 8(2014,LTS):一个版本统治了一个时代
核心特性:Lambda 表达式与函数式接口、Stream API、Optional、接口默认方法、新的日期时间 API(java.time)、Metaspace 取代永久代、Nashorn JS 引擎、Parallel 数组排序。
// JDK 8 之前 List<String> names = new ArrayList<>(); for (User u : users) { if (u.getAge() > 18) names.add(u.getName()); } // JDK 8 之后 List<String> names = users.stream() .filter(u -> u.getAge() > 18) .map(User::getName) .collect(Collectors.toList());优点:函数式写法把 Java 从"啰嗦"的边缘拉了回来;Stream 让集合操作的表达力和可读性上了一个台阶;java.time 终于结束了 Date/Calendar 的黑暗时代;Metaspace 消除了令人闻风丧胆的OutOfMemoryError: PermGen space。
缺点与坑:并行流默认用公共 ForkJoinPool,IO 密集任务用它反而拖慢全局;Optional 被滥用(当字段、当参数)反而制造了新的坑;Lambda 虽好,过度链式调用的可读性和调试难度是真实代价。
现状:2026 年的今天它仍然是装机量最大的版本,大量遗留系统、老中间件(比如某些老版本 Hadoop 生态)锁死在 8 上。Oracle 对 8 的商用更新早就收费(8u211 是分水岭,此后免费更新要自己找 OpenJDK 发行版),继续用 8 的唯一正确姿势是切换到 Temurin/Zulu/Liberica/Corretto 这类 TCK 验证的 OpenJDK 发行版。能升就升,不能升至少把发行版换掉——8 的老漏洞不会再有人免费修了。
二、JDK 9(2017):模块化——争议最大的版本
核心特性:JPMS 模块系统(Project Jigsaw)、jshell REPL、集合工厂方法(List.of())、Stream 增强(takeWhile/dropWhile/iterate 重载)、私有接口方法、多版本 jar、统一日志系统、G1 成为默认 GC。
module com.example.app { requires com.example.lib; exports com.example.api; }优点:模块系统让 JDK 自身拆成了可裁剪的模块(jlink可以打出带精简运行时的自包含镜像,容器镜像从几百 MB 缩到几十 MB);List.of()这类工厂方法现在已是日常;G1 转正为默认 GC 是后来 GC 大跃进的前奏。
缺点与坑:这是 Java 历史上破坏性最大的版本——强封装(strong encapsulation)堵死了sun.misc.Unsafe等内部 API 的直接访问,大量依赖内部 API 的老框架(反射狂魔们)集体翻车;类路径到模块路径的概念切换让构建工具链折腾了整整一年;JPMS 本身的复杂度也不低,很多团队最后只用它做 jlink 打包,应用级模块化不了了之。
现状:非 LTS,早就 EOL,没人单独用它。但它的强封装理念在 16 开始强制执行、17 全面落地,是理解后续版本封堵行为的基础。升级建议只有一句:9 的意义是通往 11 的桥,直接上 11。
三、JDK 10(2018):var 来了
核心特性:局部变量类型推断(var)、应用类数据共享(AppCDS)、并行全 GC 的 G1、容器感知(JVM 感知 cgroup 内存/CPU 限制)。
var list = new ArrayList<String>(); // 右边明确,var 减少重复 var stream = users.stream().filter(u -> u.isActive()); // 长类型链友好优点:var看着小,日常收益极大——泛型嵌套类型、长链式调用的样板代码少了一大截;容器感知是容器化时代的隐形功臣,之前的 JVM 在容器里默认看宿主机资源,动辄 OOM 被 kill,10 之后行为终于正常。
缺点:var只能用于局部变量且必须有初始化表达式,滥用会牺牲可读性(var x = getData()谁知道 x 是什么);G1 并行 Full GC 只是止血,真正的大招在后面。
现状:非 LTS,EOL。同 9,直接上 11。
四、JDK 11(2018,LTS):新节奏时代的第一个支柱
核心特性:标准 HTTP Client(支持 HTTP/2、异步)、String增强(isBlank/lines/strip/repeat)、Files.readString/writeString、基于嵌套的访问控制、Epsilon(no-op GC)、ZGC 实验性引入、JavaFX 与 Java EE 模块从 JDK 剥离、单文件源码直接运行(java Hello.java)。
HttpClient client = HttpClient.newHttpClient(); HttpRequest req = HttpRequest.newBuilder(URI.create("https://api.example.com")) .header("Accept", "application/json").build(); client.sendAsync(req, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .thenAccept(System.out::println);优点:终于有了不依赖第三方库的标准 HTTP 客户端(旧 HttpURLConnection 的时代结束);ZGC 首次亮相(目标:亚毫秒级停顿),为后续 GC 争霸埋下伏笔;脚本式运行单文件让 Java 也能写小工具。
缺点与坑:剥离 Java EE 模块(JAXB/JAX-WS/annotation 处理器相关)是升级 8→11 最大的工作量来源——老项目里javax.xml.bind一片红;Oracle JDK 从 11 起商用收费(OpenJDK 免费但 Oracle 版不提供长期二进制),License 问题第一次成为架构议题;ZGC 此时还只能用于堆 < 4TB 且实验性。
现状:LTS,企业迁移的第一站,生态(Spring Boot 2.x+、各中间件)对它的支持早已成熟。从 8 升 11 的工作量主要在依赖治理,一次投入长期受益。它是 8 用户的最低升级目标。
五、JDK 12~16:看似平淡的版本群,实则在憋大招
这五个非 LTS 版本单个看都不起眼,合起来却完成了 Java 语言近十年的两大跃迁:GC 世代更替和数据导向语法革命。快速过一遍每版的贡献:
JDK 12(2019.03):Shenandoah GC(RedHat 系低停顿 GC)实验性引入;Switch 表达式首次预览;G1 可中断混合收集。微版本。
JDK 13(2019.09):文本块(Text Blocks)首次预览;ZGC 支持堆回退(把不用的内存还给操作系统)。微版本。
// JDK 15 转正的文本块——写 SQL/JSON 不再是受罪 String sql = """ SELECT id, name FROM users WHERE status = 'ACTIVE' """;JDK 14(2020.03):Switch 表达式转正;instanceof 模式匹配预览;有用的 NPE 提示("cannot invoke ... because is null",告诉你到底哪个变量空了——日常排障幸福感提升巨大的小功能);Records 预览。
JDK 15(2020.09):文本块转正;ZGC 和 Shenandoah 双双转正(生产可用);密封类(sealed)预览;移除 Nashorn。
JDK 16(2021.03):Records转正;instanceof 模式匹配转正;强封装默认生效(--illegal-access 默认 deny,JDK 17 彻底锁死);Vector API 孵化;ZGC 支持并发线程栈处理。
// Records:一行顶过去 60 行的 POJO(equals/hashCode/toString 全有) public record Point(int x, int y) {}优点(合并说):预览→反馈→转正的节奏让语言演进来得又快又稳;Records+模式匹配+文本块三件套把样板代码压缩到极限;ZGC 生产化让"停顿 10ms 以内"从 GC 调优的奢侈品变成默认值。
缺点(合并说):非 LTS 版本半年 EOL,生产环境用它等于裸奔(官方明确不承诺安全更新);预览特性需要--enable-preview,IDE/构建工具链频繁适配;强封装对老框架的破坏从 15 开始让部分"祖传依赖"升级无门。
升级建议:这几个版本没有单独升级的意义,正确姿势是把它们当"中间站",目标是 17。
六、JDK 17(2021,LTS):生态迁移的分水岭
核心特性:密封类转正(sealed/interface permits,继承体系从此可以收口);模式匹配 switch 预览;移除 AOT/JIT 编译器(Graal);强封装内部 API彻底锁死(不再有宽松开关);macOS AArch64 支持;伪随机数生成器新接口;废弃 Applet API。
// sealed + record + switch 模式匹配(预览):代数数据类型的 Java 表达 public sealed interface Shape permits Circle, Rect {} public record Circle(double radius) implements Shape {} public record Rect(double w, double h) implements Shape {} double area(Shape s) { return switch (s) { case Circle c -> Math.PI * c.radius() * c.radius(); case Rect r -> r.w() * r.h(); }; // 无 default:编译器保证穷尽性,加新形状不改这里直接编译报错 }优点:sealed 让"谁能继承我"从文档约定变成编译器强制,配合 record 和 switch 模式匹配,Java 终于有了正经的代数数据类型表达能力;性能上比 8/11 有可观的免费提升(G1 多年的持续优化);生态拐点——Spring Boot 3、Kafka 4、各主流框架的基线都定为 17+,此后"只支持 8"的框架迅速消亡。
缺点与坑:强封装彻底锁死是双刃剑——大量依赖 JDK 内部 API 的老库(旧版反射工具、序列化框架、某些监控 agent)在此断代,--add-opens可以临时续命但不是长久之计;从 8 直升 17 的迁移工作量明显大于升 11(EE 模块移除 + 强封装 + 语法废除非兼容项三重叠加)。
现状:LTS,当前企业新项目的默认基线,也是"8 之后该去哪"的标准答案。
七、JDK 18~20:虚拟线程的孵化等待期
JDK 18(2022.03):UTF-8 成为默认字符集(跨平台编码差异从此基本绝迹);简单 Web 服务器(jwebserver)用于静态文件调试;代码片段(javadoc @snippet);互联网地址解析 SPI。微版本。
JDK 19(2022.09):虚拟线程首次预览(Project Loom 落地的历史性时刻);结构化并发孵化;Record 模式预览;外部函数与内存 API(FFM)预览。
JDK 20(2023.03):作用域值(Scoped Values)孵化;Record 模式、模式匹配 switch、FFM 第二轮预览。纯过渡版本。
这三年是 Java 平台憋大招的时期:Loom 团队把"百万级轻量并发"从概念做到预览,FFM 把 JNI 这个二十年老古董送上替代进程。单看每版内容不多,但方向感极强。
八、JDK 21(2023,LTS):近十年最重要的版本,没有之一
核心特性:
- 虚拟线程转正(JEP 444):
Thread.ofVirtual()创建的轻量线程,由 JVM 调度到少量平台线程上,阻塞时自动让出。百万并发连接的服务端编程范式从此改写; - 分代 ZGC(JEP 439):ZGC 补上分代收集,吞吐提升、堆开销下降,停顿依旧亚毫秒;
- Record 模式转正(解构 record)、模式匹配 switch 转正(JEP 441);
- 有序集合(SequencedCollection)接口——List/Deque 终于有了统一的 getFirst/getLast;
- 密钥封装机制(KEM)API、字符串模板预览(后又被撤回重做,是 21 时代少见的反复);
- 结构化并发/作用域值继续预览(Loom 第二块拼图)。
// 虚拟线程:一个 ping 应用百万连接不再是神话 try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { for (int i = 0; i < 1_000_000; i++) { int id = i; executor.submit(() -> { Thread.sleep(1000); // "阻塞"上百万个虚拟线程,只占极少平台线程 handle(id); return null; }); } }优点:虚拟线程把"一请求一线程"的朴素写法重新变成最优解——不用响应式的复杂回调链,就能拿到异步框架的吞吐量;分代 ZGC 让大堆服务的 GC 表现整体上一个台阶;switch 模式匹配转正让 sealed+record+模式匹配三件套完整成型。
缺点与坑:虚拟线程不是银弹——synchronized块内的阻塞会导致载体线程钉住(pinning),吞吐不升反降(这正是 24 要修的);连接池、ThreadLocal 滥用在高密度虚拟线程下会被放大;生态里大量库还没适配结构化并发,早期使用有 API 变动风险。
现状:LTS,强烈推荐的目标版本。Spring Boot 3.2+ 开箱支持虚拟线程,一行配置spring.threads.virtual.enabled=true,真实业务的吞吐提升案例已经遍地都是。
九、JDK 22(2024):FFM 落地与语法糖成熟
核心特性:外部函数与内存 API(FFM)转正(JEP 454)——替代 JNI 的现代方案,直接调 C 库、安全管理堆外内存;未命名变量与模式转正(JEP 456,_占位符);G1 区域钉住(JNI 临界区域不再拖死 GC);启动多文件源码程序;Stream Gatherers(自定义中间流操作)预览;结构化并发/作用域值继续预览。
// FFM:不经 JNI 直接调 C 函数 Linker linker = Linker.nativeLinker(); SymbolLookup stdlib = linker.defaultLookup(); MethodHandle strlen = linker.downcallHandle( stdlib.find("strlen").get(), FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)); try (Arena arena = Arena.ofConfined()) { long len = (long) strlen.invoke(arena.allocateFrom("hello")); }优点:FFM 转正是平台级的标志性事件——比 JNI 快(无需跨界marshal的固定开销陷阱)、安全(Arena 管理内存生命周期)、不用写 C 胶水代码;_占位符干掉了无数catch (Exception e) { /* ignore */ }里的无用变量。
缺点:FFM 生态迁移刚起步,JNI 库的替换要等各项目跟进;Gatherers 还在预览,API 可能变。
十、JDK 23(2024):文档与并发的打磨
核心特性:Markdown 文档注释(JEP 467,javadoc 终于可以用 Markdown 写了);super(...) 之前可以有语句(预览,构造器前置校验的自由);结构化并发第三轮预览(两个子任务 Joiner 模型重构);作用域值第三轮预览;ZGC 的分代模式成为默认(JEP 474,非分代模式废弃);Stream Gatherers 第二轮预览;基元类型模式预览。
优点:Markdown javadoc 对文档质量的提升立竿见影;"super 前语句"解决的不只是语法洁癖——参数校验终于能在 super 调用前做,构造不变量更早建立;分代 ZGC 转正默认,大堆服务白捡性能。
缺点:非 LTS 短命版本,结构化并发 API 重构意味着之前预览版的代码要改,跟预览特性上生产的团队会体验折腾。
十一、JDK 24(2025.03):虚拟线程补完与性能细节狂魔
核心特性:
- 虚拟线程消除 pinning(JEP 491):
synchronized块内阻塞不再钉住载体线程——21 时代最大的虚拟线程性能陷阱被正面解决,迁移遗留代码到虚拟线程的最后一块绊脚石移除; - Stream Gatherers 转正(JEP 485):
Stream.gather()自定义中间操作成为标准,流处理表达力补上最后一块短板; - Class-File API 转正(JEP 484):字节码操作的标准 API(ASM 的官方替代路线);
- 紧凑对象头(JEP 450,产品特性):对象头从 96/128 位压到 64 位以内,小对象堆占用降 10~20%,纯白捡的内存优化;
- 分代 Shenandoah 转正(JEP 404);
- AOT 类加载与链接(JEP 483):启动时间最高缩短 40%+(依赖缓存,正式 AOT JIT 在 25 继续推进);
- 后量子密码学落地(ML-KEM/ML-DSA);
- 移除 ZGC 非分代模式;废弃
sun.misc.Unsafe内存访问方法(给所有还在用 Unsafe 的库敲响丧钟)。
优点:24 是个"细节控"版本,没有炫技特性,但每一项都在还账——虚拟线程 pinning、启动速度、内存占用、流表达力,全是生产环境的真金白银。
缺点:又是一个非 LTS(2026 年 8 月已 EOL),这些好东西要等 25 LTS 才有"名分"; Unsafe 废弃意味着依赖它的老库(部分序列化/缓存框架)面临再次断代。
十二、全局观察与升级决策
12.1 十年的三条主线
回头看 8→24 这十年,Java 的演化其实是三条主线:
- 语言表达力:从 8 的 Lambda 起点,经 records/sealed/pattern matching,到 21 的三件套合璧——Java 终于是一门"写得舒服"的语言;
- 并发范式:从"线程太贵所以要用线程池+异步回调"的扭曲,回到 21 虚拟线程"一任务一线程"的朴素,代价是十年,收益是回归可读性;
- 运行时效率:G1 转正→ZGC/Shenandoah 双子星→分代化→紧凑对象头→AOT,停顿从百毫秒进到亚毫秒,启动从秒级向毫秒级推进。
12.2 升级路径决策
| 你的现状 | 建议 |
|---|---|
| 还在 JDK 8 | 最低目标 17,直接奔 21/25。工作量主要在依赖治理(EE 模块、强封装、老库),先换 OpenJDK 发行版止血 |
| JDK 11 | 升 21 几乎无痛,性能白捡,重点验证 GC 行为和强封装 |
| JDK 17 | 升 21 收益巨大(虚拟线程),注意 synchronized pinning(或直接上 24/25 免除该顾虑) |
| 新项目 | 直接 21 或 25 LTS,启用虚拟线程,写法按"一请求一虚拟线程"设计 |
12.3 发行版提醒
自 8u211/11 起,Oracle JDK 商用授权问题让"用哪个 JDK"和"用哪个版本"变成了两个问题。Temurin(Eclipse 采纳)、Amazon Corretto、Azul Zulu、BellSoft Liberica、微软 Build of OpenJDK 都是经过 TCK 验证的免费发行版,LTS 版本的安全更新周期普遍 5~8 年(Almanac 显示 8/11/17/21 至今仍在发补丁)。继续用 8 可以,但请确保你的 8 来自这些发行版而不是 Oracle 商业授权的旧安装包。
最后补一句时效:2025 年 9 月发布的JDK 25 是最新 LTS(作用域值、灵活构造器、紧凑源文件/实例 main 转正,AOT JIT 继续推进),如果你读到这篇文章时正在做新项目选型,25 值得和 21 一起放进候选。
主要参考:Oracle/OpenJDK 各版本发布说明、Java Version Almanac(javaalmanac.io,版本状态与更新时间线)、各 JEP 官方文档。性能数字为官方或社区公开口径,具体收益以自有业务压测为准。