Administrator
发布于 2019-11-29 / 652 阅读
13

Oracle JDK 收费之后,该选哪个 JDK 发行版

运维问我们:Ubuntu 服务器上装的 JDK 要不要交钱

11 月底,运维在群里发了张截图,是某篇公众号文章的标题:"Oracle JDK 开始收费,你们公司准备好被起诉了吗"。他问我们的服务器要不要处理。

我把许可协议翻了一遍,发现事情没那么吓人,但确实得做个决定。这篇是整理出来的结论。

到底改了什么

2019 年 4 月 16 日,Oracle 更新了 JDK 8 的许可协议(OTN License Agreement)。在此之前,Oracle JDK 8 个人和企业都能免费用。更新之后:

  • JDK 8u201 及以前的版本:老的 BCL 协议,可以免费用于生产环境。
  • JDK 8u211 及以后的版本:新的 OTN 协议,用于商业用途需要购买 Oracle 的 Java SE 订阅。个人学习、开发、测试仍可免费。
  • JDK 11 及以后:Oracle JDK 从一开始就是 OTN 协议,同样需要付费。

付费的标准(当时的公开报价):桌面端每人每月 2.5 美元,服务器端按处理器核心算,每核心每月 25 美元。我们线上 40 台 8 核机器,算下来一年要 9.6 万美元。这个数字一出来,结论就很明确了。

顺带一提,Oracle 的免费支持节奏也变了:

  • JDK 8 的公开更新(免费版)到 2019 年 12 月结束,商业支持到 2020 年 12 月,之后延长支持到 2025 年(但要付费)
  • JDK 11 是 LTS,2018 年 9 月发布
  • 发布周期从"几年一个大版本"改成了每 6 个月一个版本,每 3 年一个 LTS

关键:收费的是 Oracle JDK,不是 Java

这是很多人(包括一开始的我)搞混的地方。

Java 语言规范和 JVM 规范是公开的,OpenJDK 是它的开源实现,采用 GPL v2 协议(带 Classpath Exception),永久免费,可以用于商业用途。Oracle 收费的是它自己打包发布、带商业支持的 Oracle JDK 这个发行版。

打个比方:Linux 内核是免费的,Red Hat Enterprise Linux 是收费的发行版。Java 也是这个结构——OpenJDK 是上游源码,各个厂商基于它构建自己的发行版。

Oracle 自己其实也参与 OpenJDK 开发,Oracle JDK 和 OpenJDK 的代码库差异非常小(Oracle 从 JDK 11 开始就在努力让两者趋同)。主要区别是:Oracle JDK 多了一些商业特性(比如 Java Flight Recorder 在新版本里已经开源了)、有官方支持和补丁服务。

几个可选的发行版

我把当时主流的几个都装了一遍,跑了我们的服务压测对比(4 核 8 G,跑同一个订单接口 5 分钟):

发行版厂商协议免费商用我们的压测 QPS备注
Oracle JDK 8u212OracleOTN1,842需订阅
AdoptOpenJDK 8u212社区(IBM/Red Hat 等支持)GPL v21,836社区维护,最"正统"
Azul Zulu 8u202Azul SystemsGPL v21,829老牌,支持多种平台
Amazon Corretto 8u222AmazonGPL v21,841AWS 自家全量使用
Alibaba Dragonwell 8阿里巴巴GPL v21,918带阿里内部优化

性能差异在 5% 以内,基本是噪音。重要的差别在维护承诺和额外特性。

AdoptOpenJDK

社区项目,2017 年成立,由 IBM、Red Hat、Azul 等公司支持。它的定位是"提供免费、开源、社区维护的 OpenJDK 二进制包"。提供 HotSpot 和 OpenJ9 两种虚拟机可选。

# macOS 用 brew 装很方便
brew cask install adoptopenjdk8

# 或者官网下载 tar.gz
wget https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/.../OpenJDK8U-jdk_x64_linux_hotspot_8u232b09.tar.gz
tar -zxvf OpenJDK8U-jdk_x64_linux_hotspot_8u232b09.tar.gz -C /usr/local/

它的优点是中立、透明,构建脚本全部开源。缺点是没有厂商兜底(虽然背后有大公司),出了问题要靠社区。

Azul Zulu

Azul Systems 做的,是最早的商业 OpenJDK 发行版之一(2008 年就有了)。免费版叫 Zulu,付费版叫 Zulu Enterprise(带支持和认证)。它支持的平台最多,包括一些冷门的操作系统和架构。

Amazon Corretto

Amazon 2019 年 GA 的。特点是 Amazon 自己内部全量使用(上万台服务器跑的就是它),所以更新和维护投入有保障。它承诺会持续提供免费的安全更新,还提供长期的季度更新。AWS 上有一键安装。

Alibaba Dragonwell

阿里 2019 年 3 月开源的,基于 OpenJDK 8,把阿里内部用了多年的优化补丁放了出来。压测里它 QPS 最高(1,918,比 Oracle JDK 高 4.1%),主要是因为里面有几个针对电商场景的优化,比如:JWarmup(预热,提前加载热点方法减少 JIT 编译的冷启动影响)、GCIH(GC Invisible Heap,把对象放到堆外管理)、协程 Wisp(这个我们没敢用)。

我们实际试过 JWarmup,效果挺明显:服务启动后前 5 分钟的 P99 从 1,840 毫秒降到 620 毫秒。原因是它记录了上次运行时 JIT 编译的方法列表,启动时直接加载编译结果,不用重新走解释执行再触发编译的慢路径。

我们最后的选择

结论:新项目用 AdoptOpenJDK 11,存量项目迁移到 AdoptOpenJDK 8

理由:

  1. 存量项目我们跑的是 JDK 8 的代码(有大量 javax.xml.bindsun.misc.Unsafe 的依赖),直接升 11 改动太大,先平迁到 OpenJDK 8 解决合规问题。
  2. 新项目直接用 11,因为它是 LTS,能用上 HTTP Client(java.net.http)、var 局部变量类型推断(这个是 10 加的)、单文件源码直接运行这些新特性。而且 JDK 11 的 G1 比 8 里的版本成熟不少。
  3. 选 AdoptOpenJDK 而不是别家,是因为它中立、社区活跃、下载快(有国内镜像),而且我们不需要厂商的付费支持。

迁移过程

实际做完只花了两天,比预想的顺利。流程:

# 1. 下载解压
cd /usr/local
wget https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u232-b09/OpenJDK8U-jdk_x64_linux_hotspot_8u232b09.tar.gz
tar -zxvf OpenJDK8U-jdk_x64_linux_hotspot_8u232b09.tar.gz
ln -s jdk8u232-b09 java8

# 2. 改环境变量
export JAVA_HOME=/usr/local/java8
export PATH=$JAVA_HOME/bin:$PATH

# 3. 验证
$ java -version
openjdk version "1.8.0_232"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_232-b09)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.232-b09, mixed mode)

只遇到一个问题:字体渲染。我们有个生成验证码图片的功能,用的是 Font.createFont 加载自定义字体,迁移后报:

java.lang.NullPointerException
    at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)

原因是 OpenJDK 的精简版镜像默认没装字体库。装一下就好:

yum install -y fontconfig freetype
fc-cache --force

如果是 Docker 镜像,在 Dockerfile 里加:

FROM adoptopenjdk:8u232-b09-jdk-hotspot
RUN apt-get update && apt-get install -y fontconfig fonts-dejavu \
    && rm -rf /var/lib/apt/lists/*

另外提醒一句,迁移前后最好对比一下 JVM 的默认参数。不同发行版的 GC 默认值是一样的(都是 Parallel GC),但堆大小的自动计算逻辑可能有细微差别。我们用 -XX:+PrintFlagsFinal 导出了一份对比:

java -XX:+PrintFlagsFinal -version > flags.txt 2>&1

两个建议

一、别再挂着"Oracle JDK"的名字混用。有些同事本地开发装的是 Oracle JDK,服务器上是 OpenJDK,虽然代码跑起来一样,但排查问题时会平添困扰。我们统一成了 AdoptOpenJDK,并在 Dockerfile 里固定了版本 tag(不用 latest)。

二、盘点一下存量。运维用 Ansible 扫了所有机器:

ansible all -m shell -a "java -version 2>&1 | head -1" | grep -i oracle

扫出来 63 台还在用 Oracle JDK,其中 11 台跑的是 8u211 之后的版本,属于明确需要付费的范围。这 11 台优先处理。

留个问题

关于《Oracle JDK 收费之后,该选哪个 JDK 发行版》里这个坑,你当时是怎么处理的?欢迎在评论区聊聊你踩过的类似情况。

参考