欢迎光临
我们一直在努力

【Java OpenJDK】从JDK 1.0到JDK 21虚拟线程,一文读懂模块化、JRE移除、javax迁移与JDK选型

作者:大家好,我是 CodeStats。

一个在底层技术上“考古”了四年的硬核爱好者,也是 WWAIC(全周项目AI编程) 范式的提出者和实践者。我曾手写过一个完整的 Java Web 框架(从 IoC 容器到嵌入式 Tomcat,代码全开源),也喜欢用通俗的语言拆解 CPU、JVM、操作系统的运行本质。

我的技术信条:所有高深的技术,最后都能用大白话讲清楚。如果讲不清楚,说明还没真正理解。

本文核心收获:

  • 彻底搞清 OpenJDK 和 OracleJDK 的关系、许可证差异及收费真相

  • 完整梳理 Java 从 JDK 1.0 到 JDK 21 的 30 年演进脉络

  • 掌握 JDK 9 模块化前后目录结构变化,搞懂 jmods 和 lib 的分工

  • 理解 JPMS 与 OSGi 的根本差异:为什么一个禁止同名包,一个允许

  • 搞清 JDK 9+ 移除独立 JRE 的原因、运行机制及 jlink 用法

  • 吃透 JDK 21 虚拟线程的三大原理:M:N 调度、挂载/卸载、Continuation

  • 弄清楚 javax 包为什么被移除、移除了什么、由谁维护

  • 彻底分清 Java 标准 API 与内部 API 的边界,避免踩坑

目录

  • OpenJDK 和 OracleJDK 什么关系?为什么开源还被限制?

  • Java 30年演进:从 JDK 1.0 到虚拟线程

  • OpenJDK 与 OracleJDK 何时分岔?收费为何还有人选?还有哪些厂商发行版?

  • 高版本 JDK 如何选?LTS 还是非 LTS?OpenJDK 还是厂商发行版?

  • JDK 9 模块化后目录结构变了什么?jmods 和 lib 各有什么用?

  • JDK 模块化 vs OSGi:为什么 JPMS 不支持同名包而 OSGi 支持?

  • JDK 9+ 为什么不带 JRE 了?运行时从哪来?为什么还有独立 JRE 下载?

  • JDK 21 虚拟线程的本质和原理是什么?

  • javax 包为什么被移除?哪些功能没了?由谁维护?

  • Java 标准 API 和内部 API 有什么区别?

  • 一、OpenJDK 和 OracleJDK 什么关系?为什么开源还被限制?

    1.1 历史渊源

    Java 由 Sun Microsystems 发明。2006 年,Sun 在 JavaOne 大会上宣布 Java 开源,即 OpenJDK——Java SE 的开源参考实现。

    2009 年,Oracle 收购 Sun,Java 的维护权易主。

    1.2 源码层面:基本是同一个东西

    OracleJDK 基于 OpenJDK 源码构建。从 JDK 7 开始,Oracle 员工构建 OracleJDK 时,先从 OpenJDK 代码库签出代码,再合并少量私有部分。两者在功能、性能、执行逻辑上基本一致。

    1.3 核心区别:许可证

    OpenJDKOracleJDK
    许可证 GPL v2 Oracle 商业协议
    商业使用 ✅ 允许 ❌ 仅个人研究
    发布形式 源码 二进制安装包

    开源 ≠ 无限制使用。OpenJDK 的 GPL v2 允许商业使用,但 OracleJDK 的私有部分(如 Flight Recorder 内部实现)需要付费才能在生产环境使用。

    二、Java 30年演进:从 JDK 1.0 到虚拟线程

    2.1 萌芽期:JDK 1.0 – 1.1(1995-1997)

    1995 年 JDK 1.0 发布,口号 "Write Once, Run Anywhere"。JDK 1.1 引入 JDBC、内部类、反射、RMI。

    2.2 企业级时代:J2EE 1.2 – 1.4(1998-2002)

    1998 年 Java 2 发布,拆分为 J2SE(标准)、J2EE(企业)、J2ME(微型)。

    2.3 黄金十年:JDK 5 – JDK 8(2004-2014)

    JDK 5(2005):泛型、枚举、注解、自动装箱、增强 for 循环,奠定 Java 十年技术核心。

    JDK 8(2014):里程碑版本——Lambda、Stream API、新日期 API、接口默认方法。至今仍是市场占有率最高的 JDK。

    2.4 模块化革命:JDK 9(2017)

    Project Jigsaw 落地。在 package 和 jar 之间新增 模块(Module) 层级,通过 module-info.java 声明依赖和导出。JDK 自身被拆分为 70-90 个独立模块。

    2.5 LTS 时代:JDK 11 / 17 / 21

    版本时间核心特性
    Java 8 2014.03 Lambda、Stream(LTS)
    Java 11 2018.09 模块化成熟、HTTP Client、移除 Java EE(LTS)
    Java 17 2021.09 密封类、Spring Boot 3.0 最低要求(LTS)
    Java 21 2023.09 虚拟线程(LTS)

    三、OpenJDK 与 OracleJDK 何时分岔?收费为何还有人选?还有哪些厂商发行版?

    3.1 何时开始不一样?

    从 JDK 7 开始源码基本一致,真正的分水岭是 JDK 11——Oracle 停止提供独立 JRE,改变授权模式。

    3.2 收费为何还有人选?

    • Oracle 的 "一站式"商业支持

    • 法律保障(indemnification) ——专利诉讼保护

    • 企业有预算,需要长期稳定支持

    3.3 还有哪些厂商发行版?

    厂商发行版特点
    Eclipse 基金会 Adoptium Temurin 社区驱动,前身 AdoptOpenJDK
    Amazon Corretto AWS 生产验证,免费
    Azul Zulu / Zing 商业版有 C4 垃圾回收器
    Microsoft Microsoft Build of OpenJDK 官方支持
    IBM Semeru 基于 OpenJ9,云优化
    Red Hat Red Hat Build of OpenJDK 企业级支持

    全部基于 GPL v2,免费用于生产环境。

    四、高版本 JDK 如何选?LTS 还是非 LTS?OpenJDK 还是厂商发行版?

    4.1 必须选 LTS

    非 LTS 版本(如 9、10、12-20)只提供 6 个月安全补丁,生产环境必须用 LTS。

    场景推荐版本原因
    维护老项目 Java 8 生态最成熟
    企业新项目 Java 17 Spring Boot 3.0 最低要求
    技术前沿 Java 21 虚拟线程革命性特性

    4.2 选厂商发行版,不选 Oracle OpenJDK 官方版

    • Oracle OpenJDK 官方版只提供半年更新

    • 厂商发行版免费且长期支持:Temurin、Corretto、Zulu 等

    • Amazon Corretto 经过 AWS 生产验证

    五、JDK 9 模块化后目录结构变了什么?jmods 和 lib 各有什么用?

    5.1 JDK 8 vs JDK 9+ 目录对比

    对比项JDK 8JDK 9+
    独立 JRE ✅ jre/ ❌ 已移除
    核心类库 lib/rt.jar 拆分为 jmods/*.jmod
    tools.jar ✅ 存在 ❌ 已移除
    配置文件 分散各处 集中到 conf/

    5.2 jmods 和 lib 的分工

    • jmods/:构建时素材库,供 jlink 裁剪镜像。JVM 运行时不读取它。

    • lib/:运行时核心。其中的 modules 文件是内部二进制格式,包含所有标准库的 .class 字节码。JVM 启动时读取的就是它。

    六、JDK 模块化 vs OSGi:为什么 JPMS 不支持同名包而 OSGi 支持?

    6.1 OSGi 是什么?

    OSGi(2000 年代初)是 Java 模块化的事实标准,每个 bundle 通过 MANIFEST.MF 声明依赖,支持运行时热插拔。Eclipse IDE 是典型应用。

    6.2 核心差异

    维度JPMS(Java 9)OSGi
    本质 JVM 内置特性 第三方框架
    配置 module-info.java(编译时) MANIFEST.MF(运行时)
    动态性 静态,启动时确定 动态,支持热插拔
    类加载架构 分层 网状(每个 bundle 独立 ClassLoader)

    6.3 为什么 JPMS 禁止同名包,而 OSGi 允许?

    • OSGi:每个 bundle 有独立的 ClassLoader。JVM 中类的唯一标识是 (ClassLoader + 类全名) ,同名类由不同 ClassLoader 加载就是不同类,可以共存。

    • JPMS:语言级模块化,编译时就强制包唯一性。设计哲学是 "宁可启动失败,也不要运行时存在歧义" 。

    七、JDK 9+ 为什么不带 JRE 了?运行时从哪来?为什么还有独立 JRE 下载?

    7.1 为什么不带了?

    模块化让 JRE 从"固定产物"变成"可按需组装"。开发者用 jlink 按需裁剪,不再需要臃肿的通用 JRE。

    7.2 运行时从哪来?

    lib/modules 文件就是运行时的核心——它包含了所有标准库模块的 .class 字节码,JVM 启动时直接读取它。

    7.3 为什么还有独立 JRE 下载?

    第三方厂商(如 Adoptium)仍提供 JRE 格式下载,满足历史遗留部署需求。官方推荐用 jlink 自建精简运行时:

    bash

    jlink -p $JAVA_HOME/jmods –add-modules java.base –output myjre

    八、JDK 21 虚拟线程的本质和原理是什么?

    8.1 本质

    虚拟线程是 JVM 调度的轻量级线程,不再 1:1 映射 OS 线程,而是通过 载体线程(Carrier Threads) 复用 OS 线程资源。

    8.2 三大核心机制

    ① M:N 调度:M 个虚拟线程调度到 N 个载体线程(默认 N = CPU 核心数)上执行。

    ② 挂载/卸载:虚拟线程阻塞 I/O 时,自动从载体线程卸载,栈数据复制到堆内存,载体线程立即释放去执行其他任务;I/O 完成时重新挂载恢复执行。阻塞不再浪费 OS 线程。

    ③ Continuation(续体):底层通过 Continuation 保存/恢复执行状态(栈帧、局部变量),实现"暂停-恢复"。

    8.3 与平台线程对比

    维度平台线程虚拟线程
    映射 1:1 M:N
    创建成本 高(1MB 栈) 极低(几百字节)
    切换成本 微秒级(内核态) 纳秒级(用户态)
    数量上限 几千 数百万

    8.4 使用方式

    java

    // 方式1:直接创建
    Thread.startVirtualThread(() -> System.out.println("Hello!"));

    // 方式2:ExecutorService(推荐)
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> { Thread.sleep(1000); return "Done"; });
    }

    8.5 注意事项

    • 不要池化虚拟线程(创建成本极低)

    • 谨慎使用 synchronized(可能导致虚拟线程被"钉住"无法卸载)

    • 谨慎使用 ThreadLocal(数百万虚拟线程可能 OOM)

    九、javax 包为什么被移除?哪些功能没了?由谁维护?

    9.1 为什么移除?

    2017 年 Oracle 将 Java EE 捐赠给 Eclipse 基金会,更名为 Jakarta EE。为了摆脱 Oracle 对 javax.* 命名空间的控制,Eclipse 决定将包名从 javax.* 改为 jakarta.*。

    注意:javax.sql.*、javax.crypto.* 等属于 Java SE 自身,不会被移除。

    9.2 哪些被移除了?(JDK 11)

    移除模块功能
    java.xml.ws JAX-WS(Web Service)
    java.xml.bind JAXB(XML 绑定)
    java.corba CORBA
    java.transaction JTA(事务)
    java.activation JAF(MIME 处理)
    java.se.ee 上述模块的聚合

    此外,Java Applet、Java Web Start、JavaFX 也被移除。

    9.3 由谁维护?

    被移除的 Java EE 模块由 Eclipse 基金会 以 Jakarta EE 名义维护,以 jakarta.* 命名空间发布。

    JDK 11+ 如需使用,手动引入依赖:

    xml

    <dependency>
    <groupId>jakarta.xml.bind</groupId>
    <artifactId>jakarta.xml.bind-api</artifactId>
    <version>4.0.0</version>
    </dependency>

    十、Java 标准 API 和内部 API 有什么区别?

    10.1 标准 API

    Java 平台规范中定义和承诺的公共 API,所有 JDK 实现都必须提供,跨版本向后兼容。

    • 主要命名空间:java.*、部分 javax.*

    • 示例:java.util.ArrayList

    10.2 内部 API

    JDK 的内部实现细节,不属于规范,不同 JDK 实现可能不同或缺失,可能随时移除,无向后兼容保证。

    • 主要命名空间:sun.*、大部分 com.sun.*

    • 示例:sun.misc.Unsafe

    10.3 两个特例

    • sun.misc 和 sun.reflect 由 jdk.unsupported 模块导出

    • sun.misc.Unsafe 被大量框架依赖,JDK 暂时保留可访问性,但仍是内部 API

    10.4 JDK 9 强封装

    从 JDK 9 开始,模块化系统要求:只有导出(exported)或开放(opened)的包才能被外部访问,其余默认不可访问。

    10.5 开发建议

    使用 java.*,避免 sun.*,谨慎使用 com.sun.*。

    可用 jdeprscan 工具扫描代码中是否使用了已弃用或已移除的 API。

    写在最后

    Java 30 年,从"一次编写,到处运行"到虚拟线程百万并发,每一步都在解决上一代的痛点。

    版本选择:

    • 维护老项目 → Java 8

    • 企业新项目 → Java 17

    • 技术前沿 → Java 21

    JDK 选择:

    • 生产环境 → Adoptium Temurin 或 Amazon Corretto

    • 需要商业支持 → Azul Zulu 或 Oracle JDK

    技术选型没有标准答案,适合自己业务场景的,才是最好的。

    如果觉得文章对你有帮助,欢迎点赞、收藏、转发!有问题可以在评论区留言讨论。

    赞(0)
    未经允许不得转载:171主机测评 » 【Java OpenJDK】从JDK 1.0到JDK 21虚拟线程,一文读懂模块化、JRE移除、javax迁移与JDK选型
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址