苦勞德報 for Java 生態 — 2026-11-03
1. [頭版/資安] 一季五十多個 CVE、四天內兩次緊急更新:Foojay Podcast 拆解 OpenJDK 十月修補潮與 CPU/PSU/CSPU
- 作者:u/FrankCodeWriter | 10↑ | 4 則留言
- 輔助來源:
- Podcast 節目頁:Foojay Podcast #104 show notes、YouTube
- OpenJDK 一手公告:Vulnerability Advisory 2026/10/20、2026/10/21、2026/10/24、Compromised Certificates and OpenJDK Java Runtimes
- Oracle 一手公告:Critical Patch Update Advisory – October 2026、Security Alert CVE-2026-91403、Security Alert CVE-2026-114820、Security Fixing Policies
- OpenJDK 更新排程:JDK 11u wiki、JDK 8u wiki、GitHub 上的 openjdk/jdk25u、jdk21u、jdk17u、jdk11u、jdk8u tag
- Azul 方說法:Azul Zulu October 2026 Quarterly Update(Foojay)、What CSPUs Mean for Your Release Pipeline(Foojay)
報導
(本報賈新聞/科技組報導)十月對跑 Java 的維運團隊來說是很忙的一個月。10 月 20 日的季度安全更新一口氣修了數十個漏洞,隔天和四天後又各補了一次 out-of-cycle 更新,兩次都跟印度 CA 業者 eMudhra 的憑證外洩有關。Foojay Podcast 第 104 集找來 Azul Deputy CTO、OpenJDK Vulnerability Group 成員 Simon Ritter,把這一輪更新從頭講一遍;主持人 Frank Delporte 把節目貼到 r/java。
先揭露利益關係,發文者在內文第一行就寫了:主持人與來賓都是 Azul 員工,Foojay Podcast 是 Azul 贊助的社群專案。Azul 本身販售付費的 OpenJDK 支援,節目後段也花了不少篇幅談「為什麼要付錢買有支援的 build」。下文凡是來自 Podcast 或 Azul 文章的說法,都會標明出處;版本號與 CVE 數量則以 OpenJDK 與 Oracle 的一手公告為準。
這則貼文在 r/java 只拿到 10 分、4 則留言,其中一則還是 AutoModerator 誤判成求助文而自動移除的通知。熱度不高,但內容對 SRE 的實用性高,本報選為頭版。
這是哪一次更新
| 日期 | 事件 | 一手來源 |
|---|---|---|
| 2026-10-20(週二) | 季度更新:Oracle October 2026 CPU 與 OpenJDK 同步釋出 | OpenJDK 2026/10/20、Oracle cpuoct2026 |
| 2026-10-21 | 第一次 out-of-cycle:CVE-2026-91403,將 eMudhra 受影響的中繼 CA 憑證設為不信任 | OpenJDK 2026/10/21、Oracle Alert |
| 2026-10-24 | 第二次 out-of-cycle:CVE-2026-114820,把相關的 root CA 憑證從 JDK 內建 keystore 移除 | OpenJDK 2026/10/24、Oracle Alert |
兩份 out-of-cycle 公告的最後更新時間都是 10 月 26 日,內文的「Advice to users」都指向 openjdk.org/emudhra。
53 個 CVE 是怎麼算的
「53」是 Azul 的算法,不同來源的數字並不一致,本報整理如下:
| 來源 | 數字 | 組成 |
|---|---|---|
| Azul(Foojay 文章、Podcast) | 53 | 核心 OpenJDK 47 個 + JavaFX 6 個;2 個 high、37 個 medium、14 個 low |
| OpenJDK advisory 2026/10/20(本報逐列清點) | 54 | OpenJDK risk matrix 48 個 + OpenJFX risk matrix 6 個 |
| Oracle CPU October 2026,Java SE risk matrix | 55 | Oracle 自述「55 new security patches for Oracle Java SE」 |
差異的一部分可以從公告本身看出來:Oracle 的清單多了 CVE-2026-91484(Java plugin,只影響 Oracle 8u503),這個元件不在 OpenJDK 裡。Azul 文章也提到 Oracle 的數字含 GraalVM 與 Oracle 專有元件。至於 Azul 的「核心 47 個」比 OpenJDK matrix 的 48 個少一個,原因本報未能查到。
不管用哪個數字,跟過去比都是暴增。Podcast 與 Azul 都引用「過去平均每季 7.6 個」這個基準。本報對照 2026/07/21 advisory,七月是 OpenJDK 11 個加 OpenJFX 7 個,共 18 個。Ritter 在節目中把增加的原因歸給 AI:他提到 Anthropic 年初發表的 Claude Mythos 這類專門找漏洞的模型開始被用來分析 Java,問題多半是被防守方先找到、先修掉。這是他的解讀,本報未另行查證。
嚴重度、元件與遠端可利用性
季度更新本身沒有 critical 等級的漏洞:
- OpenJDK 部分最高 7.0:CVE-2026-91415(client-libs/2d),網路可達、不需權限、不需使用者互動,但攻擊複雜度為 High。
- 全部最高 7.5:CVE-2026-43660(OpenJFX javafx/web,Oracle 標為 JavaFX (WebKitGTK)),需要使用者互動。
- 兩次 out-of-cycle 的 CVE-2026-91403 與 CVE-2026-114820 都是 9.1,向量為 AV:N/AC:L/PR:N/UI:N/C:H/I:H/A:N(OpenJDK 10/21、10/24)。Ritter 在節目中說是 10.0,與公告不符,以公告為準。
遠端可利用方面,Oracle 的公告寫明 55 個中有 49 個「may be remotely exploitable without authentication」。OpenJDK advisory 的向量欄是八個字母的縮寫,本報拿它跟 Oracle 的完整欄位逐筆對照,確認順序是 AV、AC、PR、UI、S、C、I、A。依此清點,48 個 OpenJDK CVE 中有 43 個是「網路可達且不需權限」,其中 40 個也不需要使用者互動。
48 個 OpenJDK CVE 的元件分布:
| 元件群 | 數量 | 重點子元件 |
|---|---|---|
| core-libs | 16 | java.net 就占 10 個,另有 java.util.jar、java.sql、java.rmi、java.io、java.util |
| security-libs | 13 | java.security 6、org.ietf.jgss(JGSS/Kerberos)4、javax.net.ssl(JSSE)2、javax.security 1 |
| client-libs | 7 | 2d 3、javax.imageio 2、javax.sound、java.awt |
| hotspot | 4 | 全部是 compiler |
| xml | 3 | jaxp |
| tools、core-svc、other-libs | 5 | jlink、jshell、core-svc、corba |
對伺服器端維運來說,java.net、JSSE、java.security、JAXP 這幾類最值得注意,因為服務一定會碰網路、TLS 和 XML。2D 與 imageio 則跟伺服器端產報表、處理上傳圖片有關(這是本報推論)。
兩次 out-of-cycle:eMudhra 憑證事件
OpenJDK 的 eMudhra 說明頁開頭寫道:eMudhra 有八張中繼 CA 憑證的私鑰外洩。依 Azul 文章的說法,Mozilla 在 10 月 20 日揭露此事,剛好和季度更新同一天。拿到這些私鑰的人可以替任何網域簽出能串到受信任 root 的憑證;Java 預設不檢查憑證撤銷,所以在 JDK 封鎖之前會照單全收。
修補分兩步:
- 10/21,CVE-2026-91403:Oracle 公告說這次更新「distrusts」受影響的中繼 CA 憑證。
- 10/24,CVE-2026-114820:Oracle 公告說這次是「remove」相關的 root CA 憑證。OpenJDK 說明頁要求讀者確認 release note 裡有 JDK-8395690/CVE-2026-114820,並提醒「即使過去幾天已經更新過」也要再處理一次。
為什麼一次不夠?Ritter 在節目中的說法是:第一次修補是把舊憑證的指紋列入封鎖,之後有人指出,換個參數重新產生的憑證不會被這份封鎖清單擋下,所以第二次直接把 root CA 從 cacerts 拿掉。他自己在節目中也說記不清所有細節,這段僅供參考。
各 LTS 線的修補版本
OpenJDK upstream 的 25、21、17(以及非 LTS 的 27)在 GitHub 上都有三次更新各自的 -ga tag:
| 版本線 | 10/20 季度更新 | 10/21 修 CVE-2026-91403 | 10/24 修 CVE-2026-114820 | 出處 |
|---|---|---|---|---|
| 25 | 25.0.5 | 25.0.5.0.2 | 25.0.5.0.3 | openjdk/jdk25u tag jdk-25.0.5-ga、jdk-25.0.5.0.2-ga、jdk-25.0.5.0.3-ga |
| 21 | 21.0.13 | 21.0.13.0.2 | 21.0.13.0.3 | openjdk/jdk21u 同格式 tag |
| 17 | 17.0.21 | 17.0.21.0.2 | 17.0.21.0.3 | openjdk/jdk17u 同格式 tag |
| 27(非 LTS) | 27.0.1 | 27.0.1.0.2 | 27.0.1.0.3 | openjdk/jdk27u 同格式 tag |
這張表和 OpenJDK 公告的「affected versions」對得上:10/21 公告列 25.0.5、21.0.13、17.0.21 為受影響版本,10/24 公告列 25.0.5.0.2、21.0.13.0.2、17.0.21.0.2 為受影響版本。對 25/21/17 來說,修完兩次 eMudhra 的 upstream 版本是 x.0.x.0.3。
11 和 8 的狀況比較複雜,本報查到的資料如下:
| 版本線 | OpenJDK upstream 排程 | 截至 11/3 GitHub 上觀察到的 tag | Oracle Java SE | 出處 |
|---|---|---|---|---|
| 11 | 10/20 GA 11.0.33(jdk-11.0.33-ga,預計是 +9);11/17 GA 11.0.33.1 |
有 jdk-11.0.33+9、+10,沒有 jdk-11.0.33-ga,最新的 -ga 仍是 11.0.32.1 |
10/20 為 11.0.33;10/24 公告列 11.0.33.0.2 為受影響版本 | JDK11u wiki、jdk11u、Oracle Alert 114820 |
| 8 | 10/20 GA 8u512(jdk8u512-ga,預計是 b08);11/17 GA 8u514 |
有 jdk8u512-b08、b09,沒有 jdk8u512-ga,最新的 -ga 仍是 8u504 |
10/20 為 8u511;10/24 公告列 8u511-b32 為受影響版本 | JDK 8u wiki、jdk8u、Oracle Alert 114820 |
幾點要說明:
- 三份 OpenJDK advisory 對 11 與 8 列的受影響版本始終是 11.0.32.1 與 8u504,沒有隨 out-of-cycle 更新。11 與 8 在 upstream 修完 eMudhra 的版本號,本報未能查到。
- Oracle 的 8u 版本號(8u511)跟 OpenJDK upstream(8u512)本來就不同,比對時不要混用。
- Oracle 的 Security Alert 只列「受影響版本」,修補後的確切版本號寫在需要登入的 Patch Availability Document 裡,本報未能查到。
- 各家發行版(Temurin、Corretto、Zulu、Red Hat 等)的實際版本號見下方發行版對照。
CPU、PSU、CSPU 到底差在哪
這三個縮寫連業界自己都混著用。以下依 Podcast 中 Ritter 的說明、Oracle 文件與 Azul 文章整理:
| 名稱 | 全名 | 內容 | 節奏 | 出處 |
|---|---|---|---|---|
| CPU | Critical Patch Update | 只有安全修補,變動最小,可以最快上線 | 每季,一、四、七、十月的第三個週二 | Ritter(Podcast 32:40 起);Azul 文章:建立在上一季 PSU 之上 |
| PSU | Patch Set Update | 完整更新:安全修補,加上 bug fix、效能改進等所有變更 | 每季,與 OpenJDK 季度釋出對齊 | Ritter(Podcast);Azul 文章 |
| CSPU | Critical Security Patch Update | 新的每月更新,只有安全修補,排在兩次季度更新之間 | 每月第三個週二 | Oracle Security Fixing Policies 月曆:1/4/7/10 月為 CPU,其餘月份為 CSPU |
容易踩到的有三點:
- 名稱錯位。Ritter 說,許多發行版把自己提供的東西叫 CPU,但內容其實是完整更新,技術上應該叫 PSU。Azul 文章也寫明 Oracle 所謂的「CPU」對應的是 Azul 的 PSU,含 bug fix。看到「CPU」三個字,要確認內容是只有安全修補,還是完整更新。
- 回歸風險。Ritter 與 Azul 文章都提到,過去約五次完整更新(PSU)就有一次引入回歸。這是 Azul 主張「安全修補走快線、完整更新走慢線」的理由。
- CSPU 的版本號多一位。Foojay 的 CSPU 文章說「CSPU releases append a digit onto the version」。實例是 8 月 18 日第一次 Java CSPU,upstream 版本為 25.0.4.1、21.0.12.1、17.0.20.1、11.0.32.1、8u504(2026/08/18 advisory 與 GitHub
-gatag)。
下一次 CSPU 是 11 月 17 日,Oracle 公告列出的接下來四個日期是 2026/11/17(CSPU)、12/15(CSPU)、2027/1/19(CPU)、2/16(CSPU)。OpenJDK 11u 與 8u wiki 也排了 11/17 釋出 11.0.33.1 與 8u514。另外,jdk25u 已經出現 jdk-25.0.5.1+1 這個 build tag,本報推論是為 11/17 CSPU 準備,尚未正式釋出。
各發行版與 Docker Hub 現況
先講比較基準。這次是 2026-10-20 的季度更新,OpenJDK advisory 列了 48 個 OpenJDK CVE 加 6 個 OpenJFX CVE;之後 10-21、10-24 又各發了一份 out-of-cycle advisory,都是 CVSS 9.1,10-24 那份處理的是 eMudhra 中繼 CA 私鑰外洩(CVE-2026-114820)。upstream 的 GA tag 依序是:
- 25:25.0.5 → 25.0.5.0.2 → 25.0.5.0.3
- 21:21.0.13 → 21.0.13.0.2 → 21.0.13.0.3
- 17:17.0.21 → 17.0.21.0.2 → 17.0.21.0.3
- 11、8:截至 11-03,GitHub
openjdk/jdk11u、openjdk/jdk8u只有jdk-11.0.33+10、jdk8u512-b09這類 build tag,還沒有-gatag;最新的 GA 仍是 11.0.32.1 與 8u504
所以下表「是否已含修補」拆成兩層:含 10-20 季度更新,以及含 out-of-cycle 修補(以 x.0.3 或廠商標示含 CVE-2026-114820 為準)。查詢時間是 2026-11-03,距離季度更新過了 14 天,距離 10-24 的 out-of-cycle 過了 10 天。
| 發行版 | 這次的修補版本(25 / 21 / 17 / 11 / 8) | Image | 浮動 tag 實際版本(25 / 21 / 17 / 11 / 8) | 含季度更新 | 含 out-of-cycle | Image 最後更新 |
|---|---|---|---|---|---|---|
| Eclipse Temurin | 25.0.5+8 / 21.0.13+9 / 17.0.21+9(10-22);11、8 尚未釋出 | eclipse-temurin |
25.0.5+8 / 21.0.13+9 / 17.0.20.1+1 / 11.0.32.1+1 / 8u504-b01 | 25、21 是;17、11、8 否 | 全部否(Temurin 還沒出 x.0.3) | 25、21:10-29;17、11、8:10-27 |
| Amazon Corretto | 10-20:25.0.5.0.9 / 21.0.13.0.10 / 17.0.21.0.10 / 11.0.33.0.9 / 8.512.08.1;10-24:25.0.5.0.10 / 21.0.13.0.11 / 17.0.21.0.11 / 11.0.33.0.10 / 8.512.09.1 | amazoncorretto |
25.0.5.0.10 / 21.0.13.0.11 / 17.0.21.0.11 / 11.0.33.0.10 / 8u512-b09 | 全部是 | 全部是 | 11-02 |
| Azul Zulu | 25.0.5.0.3 / 21.0.13.0.3 / 17.0.21.0.3 / 11.0.33 / 8u512 | azul/zulu-openjdk |
25.0.5+8 / 21.0.13+9 / 17.0.21 / 11.0.33 / 8u512-b08 | 全部是 | 25、21、17 否;11、8 未能確認 | 10-29 |
| BellSoft Liberica | 10-21:25.0.5 / 21.0.13 / 17.0.21 / 11.0.33 / 8u512;10-26~27:25.0.5.0.3 / 21.0.13.0.3 / 17.0.21.0.3 / 11.0.33.0.3 / 8u513 | bellsoft/liberica-openjdk-debian |
25.0.5.0.3 / 21.0.13.0.3 / 17.0.21.0.3 / 11.0.33.0.3 / 8u513-b09 | 全部是 | 全部是 | 10-28 |
| SAP SapMachine | 10-20:25.0.5 / 21.0.13 / 17.0.21;10-26:25.0.5.0.3 / 21.0.13.0.3 / 17.0.21.0.3;不出 11、8 | sapmachine |
25.0.5.0.3 / 21.0.13.0.3 / 17.0.21.0.3 / 11.0.30 / 無 | 25、21、17 是;11 否 | 25、21、17 是 | 25、21、17:10-27;11:2026-03-17 |
| Microsoft Build of OpenJDK | 25.0.5.0.2 / 21.0.13.0.2 / 17.0.21.0.2;11 停在 11.0.32.1;不出 8 | mcr.microsoft.com/openjdk/jdk(不在 Docker Hub) |
25.0.5.0.2 / 21.0.13.0.2 / 17.0.21.0.2 / 11.0.32.1 / 8u504(Temurin) | 25、21、17 是;11、8 否 | 部分(含 10-21,10-24 未能確認) | 11-02 |
| Red Hat build of OpenJDK | 官方 release notes 查不到 | registry.access.redhat.com/ubi9/openjdk-{25,21,17}、ubi8/openjdk-8(不在 Docker Hub) |
25.0.5 / 21.0.13 / 17.0.21 / 11.0.25 / 8u504 | 25、21、17 是;11、8 否 | 未能確認 | 25、21、17、8:10-30;11:2024-12 |
| Oracle JDK | 25.0.5.0.3 / 21.0.13.0.3 / 17.0.21.0.3 / 11.0.33.0.3 / 8u511(10-24) | 不在 Docker Hub,未查 | 未查 | 未查 | 未查 | 未查 |
openjdk(Docker 官方 image,已 deprecated) |
不適用 | openjdk |
浮動 tag 已刪除 | 否 | 否 | 非 EA tag 最後一次是 2023-02-08 |
Image 實際版本的來源:Corretto、Liberica、SapMachine、Zulu 是讀 registry 上 image config 的 ENV 與 build history,Temurin 是 JAVA_VERSION 環境變數,Microsoft、Red Hat、Zulu 21 / 11 / 8 與 Temurin 17 是直接 docker run ... java -version 取得(皆為 linux/amd64)。
已經跟上的
Corretto 與 Liberica 最完整:五條 LTS 的浮動 tag 都已經換成含 10-24 修補的版本。Corretto 的 changelog 把 CVE-2026-91403 直接列在 10-20 那版,10-24 再出一版補 CVE-2026-114820。SapMachine 的 25、21、17 也已經是 x.0.3。
落後或有缺口的
- Temurin 是這次最需要注意的。Adoptium API 顯示 17.0.21+9 在 10-22 就釋出了,但
eclipse-temurin:17到 11-03 還是 17.0.20.1+1,晚了 12 天。11 與 8 是 Adoptium 本身還沒出(Adoptium support 頁面寫 10 月預計是 jdk-11.0.33 與 jdk8u511,並註明會比排程晚一點)。三次 out-of-cycle 修補 Temurin 一個都還沒出。用eclipse-temurin:17、11、8的人,現在拿到的仍是七、八月的版本。 - Zulu 的套件其實已經有 25.0.5.0.3、21.0.13.0.3、17.0.21.0.3(Azul metadata API 與 apt repo 都看得到),但 Docker Hub 的 image 沒跟上。原因從 Dockerfile 看得出來:它用
zulu21-jdk=21.0.13-*指定版本,這個樣式比對得到 apt 裡的21.0.13-3,比對不到21.0.13.0.3-5(這是本報依 apt 版本比對規則的推論,實際跑出來的版本 Zulu21.54+107 也吻合)。Zulu 11、8 在 Azul metadata 裡沒有 x.0.3 後續版,是否已含 10-24 的修補,查不到明確說明。 - Microsoft 的 release notes 說這次「經過幾次 respin,最終版本定為 25.0.5.0.2」,並且列了 10-20、10-21、10-24 三份 advisory。但 10-24 advisory 把 25.0.5.0.2 列為受影響版本,兩邊說法對不上。本報在
mcr.microsoft.com/openjdk/jdk:25-ubuntu的 cacerts 裡還看得到 eMudhra 的emSign Root CA - G1,已經是 x.0.3 的 Liberica、SapMachine、Corretto 則看不到(推論:Microsoft 這版很可能還沒處理 eMudhra,但沒有官方說明證實)。Microsoft 11 停在 11.0.32.1;8-azurelinux裝的是 Temurin 8u504。 - Red Hat 的 ubi9 25、21、17 已是 10-20 的版本號,out-of-cycle 部分查不到 Red Hat 的說明(Red Hat image 的 cacerts 由系統 CA 套件管理,不能拿 JDK 的 cacerts 判斷)。
ubi9/openjdk-11:latest和ubi8/openjdk-11:latest都還是 11.0.25,image 建立於 2024-12、2025-01,看起來已經停止更新。 - SapMachine 官方維護表把 11 列為 2024-12 EOL,
sapmachine:11停在 11.0.30。17 在表上是 2026-09 EOL,但這次仍出了 17.0.21.0.3 與 image。
openjdk: image 已經拉不下來
openjdk 這個 Docker 官方 image 從 2022 年 7 月起就 deprecated,頁面上寫著只剩 Early Access 會繼續更新,並建議改用 amazoncorretto、eclipse-temurin、ibm-semeru-runtimes、ibmjava、sapmachine。2025-11 的 docker-library/openjdk PR #550 進一步把浮動 tag 刪掉,只保留明確的歷史版本與 EA / RC tag。實測 openjdk:latest、openjdk:21、openjdk:17、openjdk:11、openjdk:8、openjdk:17-jdk-slim 都回 no such manifest。還留著的 openjdk:17.0.2 是 2022-04 的、openjdk:11.0.16 與 openjdk:8u342 是 2022-08 的。
Docker Hub 上 openjdk 的 last_updated 顯示 10-28,那是 28-ea 的 EA build 在更新,跟正式版無關,不要被這個日期誤導。CI 或 Dockerfile 裡如果還寫著 FROM openjdk:17,現在應該已經 build 不起來;如果 pin 的是 openjdk:17.0.2 這種歷史 tag,build 得過,但 JDK 是四年前的版本。 ← 藏鏡人批:FROM openjdk:17 還留在 Dockerfile 裡的,今天就去改。
浮動 tag 與固定版本 tag
- 浮動 tag(
21、21-jre、17-jdk-noble)會跟著重新推送,但什麼時候推、推的是哪一版,由 image 維護者決定。上面 Temurin 17、Zulu 21 的例子說明:發行版出了新版,不代表浮動 tag 已經換過去。 - Docker Hub 的 last_updated 也不能拿來判斷 JDK 版本。
eclipse-temurin:17在 10-27 更新過,但 JDK 還是 17.0.20.1,那次是 base OS 重新 build。 ← 藏鏡人批:浮動 tag 不保證你拿到新的 JDK,digest 和java -version才是事實。 - 固定版本 tag(
eclipse-temurin:21.0.13_9-jre、amazoncorretto:21.0.13)比較好追,但 Corretto、Zulu 的 tag 只到三段版本號,amazoncorretto:21.0.13在 10-20 與 10-24 之後指向的是不同 build,要確認還是得看 image 內容或 digest。 - 想完全固定,就 pin digest(
image@sha256:...),再交給 Renovate、Dependabot 之類的工具更新。
怎麼確認實際跑的版本
直接在 image 裡跑 java -version:
docker run --rm eclipse-temurin:21-jre java -version
Temurin 的 image 另外有 JAVA_VERSION 環境變數可以看:
docker run --rm --entrypoint printenv eclipse-temurin:21-jre JAVA_VERSION
看 registry 上目前 tag 指向的 digest(不用 pull),以及本機 image 的 digest:
docker buildx imagetools inspect eclipse-temurin:21-jre
docker image inspect --format '{{index .RepoDigests 0}}' eclipse-temurin:21-jre
叢集裡列出每個 pod 實際跑的 image digest,再對特定 pod 跑 java -version:
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.status.containerStatuses[*].imageID}{"\n"}{end}'
kubectl exec <pod> -- java -version
要判斷 10-24 的 eMudhra 問題,OpenJDK 的說明頁提供 CheckCerts.java 可以檢查執行中的 JDK;暫時沒有更新版可用時,說明頁也列了移除受影響根憑證、開啟 revocation 檢查兩種替代做法。
出處
- OpenJDK Vulnerability Advisories 列表
- OpenJDK Vulnerability Advisory 2026-10-20
- OpenJDK Vulnerability Advisory 2026-10-21
- OpenJDK Vulnerability Advisory 2026-10-24
- OpenJDK eMudhra 說明頁
- upstream GA tag:openjdk/jdk25u、openjdk/jdk21u、openjdk/jdk17u、openjdk/jdk11u、openjdk/jdk8u
- Adoptium API:Temurin 21 GA releases
- Adoptium Support(發布排程)
- Amazon Corretto changelog:25、21、17、11、8
- Azul Metadata API
- Azul apt repository
- BellSoft Liberica GitHub Releases
- SapMachine GitHub Releases
- SapMachine Maintenance and Support
- Microsoft Build of OpenJDK release notes
- MCR openjdk/jdk tag 列表
- Oracle Java current releases(Java Management Service API)
- Docker Hub:eclipse-temurin、amazoncorretto、azul/zulu-openjdk、bellsoft/liberica-openjdk-debian、sapmachine、openjdk
- docker-library/openjdk#505(deprecation)
- docker-library/openjdk#550(移除浮動 tag)
- Red Hat build of OpenJDK 21 文件
維運行動清單
以下每一項都附上依據,沒有出處的建議本報不寫:
- 確認版本。在主機上跑
java -version;容器則看 image 裡實際的 JVM 版本,例如docker run --rm <image> java -version。拿結果對照上方 upstream 版本表;各發行版的對應版本號見上方發行版對照。依據:Podcast 結論「check your Java version」、Azul 文章。 - 直接上 10/24 那一版。還沒開始更新的,直接跳到修完 CVE-2026-114820 的版本(upstream 為 25.0.5.0.3、21.0.13.0.3、17.0.21.0.3);10/20 或 10/21 已經上線的,要再更新一次。依據:OpenJDK eMudhra 頁「even if you have updated your runtime in the past few days」、Azul 文章「supersede both」。
- 確認 release note 有 JDK-8395690/CVE-2026-114820。這是 OpenJDK 判斷是否修好的唯一標準,比版本號更可靠。依據:OpenJDK eMudhra 頁。
- 先用 CheckCerts.java 檢查。OpenJDK 提供的檢查程式在 JDK 8 以上都能跑,可以先盤點哪些 runtime 有風險。依據:同上。
- 暫時還不能更新時,依 OpenJDK 頁面的步驟處理:
- 備份 cacerts,用
keytool -delete -cacerts -alias "<alias>"移除四個 root 別名:emsignrootcag1 [jdk]、emsignrootcag2 [jdk]、emsigneccrootcag3 [jdk]、comodoaaaca [jdk]。JDK 8 的路徑是$JDK_HOME/jre/lib/security/cacerts。 - 或者對 TLS 應用加上
-Dcom.sun.net.ssl.checkRevocation=true -Dcom.sun.security.enableCRLDP=true啟用撤銷檢查。 - 每個獨立安裝的 runtime 都要做一次,做完要重新啟動執行中的 JVM。
- 備份 cacerts,用
- OS 套件裝的 JDK 規則不一樣。OpenJDK 頁面寫明:Arch、Fedora、Gentoo、Oracle Linux、RHEL 的 OpenJDK 套件用 OS 的憑證,更新 OS 憑證多半就夠。Debian、Ubuntu、Devuan 因為
ca-certificates-java的長年 bug,光更新套件不夠,要以 root 執行rm -f /etc/ssl/certs/java/cacerts和update-ca-certificates --fresh。FreeBSD 與 Homebrew 不用 OS 憑證,要等套件本身更新。依據:OpenJDK eMudhra 頁。 - 把 patch 流程改成每月。CSPU 每月第三個週二出貨,下一次是 11/17。Foojay 的 CSPU 文章建議把 CSPU 當成季度 CPU 一樣處理,走同一套測試與上線流程,並確認 pipeline 可以每月觸發、不卡人工。依據:Oracle cpuoct2026 日程、Foojay CSPU 文章。
- 更新順序:安全修補走快線,完整更新走慢線。Azul 文章與 Ritter 都建議 security-only 的更新快速驗證、快速部署,含 bug fix 的完整更新另外排時間測試。請留意這是 Azul 的立場,Azul 自家同時賣這兩種 build;採用前先確認你用的發行版到底提供哪一種。
本報觀點:這一輪更新真正的壓力不是 53 這個數字,而是節奏變了。過去一季處理一次就好,現在一個月內可能先有季度更新,接著兩次 out-of-cycle。對 SRE 來說,與其追著每次公告手動處理,不如把「base image 自動重建、跑測試、推上線」這條路打通,這也是本則貼文底下唯一一則實質留言的經驗。另一方面,節目來自販售付費支援的廠商,「免費 build 不安全」的論點要分開看:本報查到的 upstream tag 顯示,25/21/17 的社群版本確實跟上了兩次 out-of-cycle;11 與 8 的 upstream 正式 GA 狀況則仍待確認。
社群反應
| 觀點 | 說明 | 代表留言 |
|---|---|---|
| 容器化讓每月更新變輕鬆 | 唯一一則實質討論,主張自動重建 base image 就不在乎更新頻率 | 「改成容器化 build 之後,轉到每月安全更新其實很無痛。只要 base image 會自動重建、跑過測試套件,上游是季度更新還是 out-of-cycle 根本沒差。」(1↑) |
| 機器人誤判 | 貼文一度被 AutoModerator 當成求助文移除 | 「看起來你在 /r/java 的這篇貼文是在找程式或學習上的協助……你的貼文已被移除。」(-7↑) |
| 作者澄清 | 發文者回應貼文是在提供資訊 | 「不,這篇不是在求助,是在提供 Java 安全更新的資訊。」(4↑) |
| 吐槽機器人 | 網友對誤判的反應 | 「笨機器人。」(2↑) |
2. [技術] 用 Go 重寫 Kotlin 編譯器 kc,warm 狀態比 kotlinc 快 10 倍以上、記憶體只要十分之一
- 作者:u/Curious-Flower-7361 | 66↑ | 55 則留言
- 輔助來源:
報導
(本報賈新聞/工具組報導)r/Kotlin 網友 u/Curious-Flower-7361 發表以 Go 撰寫的 Kotlin JVM 編譯器 kc,宣稱在 warm 狀態下(模擬 Gradle daemon 常駐的情境)比 kotlinc 2.4.21 快一個數量級。作者公布的三組數據:kotlinx-coroutines-core 從 2.50 秒降到 159 毫秒(16 倍)、Ktor 的 ktor-io、ktor-utils、ktor-http 三個 module 從 2.32 秒降到 226 毫秒(10 倍)、kotlinpoet 從 1.23 秒降到 97 毫秒(13 倍)。記憶體方面,kc 約用 100 MB,kotlinc 約 1 GB。
作者解釋動機:現有的 linter、KSP、language server 幾乎都建在 kotlinc 的 frontend 上,速度天花板就是 kotlinc 本身。如果 frontend 夠快,linter 可以從秒級降到毫秒級、LSP 反應更即時、KSP annotation processing 大幅加速;CI 每次都要冷啟動 JVM,也能省下時間;另外,coding agent 在一次任務裡要反覆「編譯、看錯誤、修改」很多輪,編譯回饋越快,迴圈越短。作者也在留言中承認,TypeScript 編譯器 tsc 改用 Go 重寫是他的靈感來源。
作者也列出目前做不到的事:完全沒有 warning,錯誤也只有型別檢查階段抓得到的那些,diagnostics 測試與 kotlinc 一致率 79%;box tests 通過 95%;部分語言功能尚未支援;只支援 JVM,沒有 Gradle plugin 與 IDE 整合。雖然有 -compose、-parcelize、-serialization 等 flag,作者表明還沒試過完整的 Android build。要試用需要 Go 1.27.2 以上與 JDK,作者定位為 prototype,會持續開發到能支撐多數真實專案。
社群反應兩極。最高票留言直指「沒做完的功能」本身就是加速來源,這樣比較並不公平;另有人認為剩下 5% 沒過的測試往往才是最複雜的功能。程式碼風格與「一個巨大 commit」也讓多位網友判斷這是 AI 生成,批評作者沒有揭露。也有網友提出更公平的對照組:拿 GraalVM native image 版的 kotlinc 來比。作者隨後補測,native image 版 kotlinc 在速度上與 warm JVM 相當(kotlinpoet 1.17 秒、coroutines-core 2.51 秒),記憶體降到 240 至 350 MB,仍高於 kc 的 85 至 120 MB;cold JVM 則要 7 到 19 秒。另一派則從架構角度提醒,JVM 的取捨是刻意的,tsc 的 Go 版本雖然快,但對外 API 與擴充性遠不如原版,Kotlin 的 compiler plugin 生態若要搬到重寫版,難度不低。
本報觀點:kc 的價值不在於取代 kotlinc,而在於證明了「frontend 可以快一個數量級」。對每天開著 IDE 等 linter、或在 CI 冷啟動上燒時間的團隊來說,這個數字很誘人。但 79% 的 diagnostics 一致率意味著它目前還不能當作「這段程式碼能不能編過」的可靠裁判,而 Kotlin 生態高度依賴 compiler plugin,這是重寫版最難跨越的門檻。社群質疑 AI 生成與比較基準的聲音雖然刺耳,卻也點出這類專案必須回答的兩個問題:剩下的 5% 補不補得完,以及補完之後還剩多少速度優勢。 ← 藏鏡人批:最後 5% 通常比前面 95% 還難寫,快是因為還沒做完的那部分也算進去了。
社群反應
| 觀點 | 說明 | 代表留言 |
|---|---|---|
| 比較基準不公平 | 少做功能本來就會快,不能直接對比 | 「『還沒做的事』那段很重要。拿掉編譯器的功能當然能換到速度,但這不是公平的比較。」(79↑) |
| 剩下 5% 才是難處 | 沒通過的測試往往驗證最複雜的功能 | 「那 5%(說實話已經很多了)沒通過的編譯器測試,影響可能相當大。」(31↑) |
| 質疑 AI 生成未揭露 | 從程式碼風格與單一 commit 判斷 | 「從程式碼看起來,是 Claude 寫的,不是你。典型的 AI 垃圾程式碼:程式碼過度精簡、註解卻過度冗長。」(38↑) |
| JVM 的取捨是刻意的 | 速度之外還有架構與擴充性 | 「tsc 用 Go 重寫了,API 跟原版 TypeScript 比起來糟透了。大家得重新發明更爛的方案來擴充編譯器。」(28↑) |
| 提議與 GraalVM 對照 | 不靠重寫,改用 native image 比較 | 「能不能拿 GraalVM native 版的 kotlinc 跟它比?這樣就不依賴 AI 重新實作編譯器邏輯。」(2↑) |
| 支持繼續做下去 | 功能完整又快的編譯器是大好事 | 「做得好,別理這裡討厭 AI 的人。一個功能完整、快很多的 Kotlin 編譯器絕對是好事,繼續加油!」(3↑) |
3. [技術] Hibernate 吃掉我的 UPDATE:graceful shutdown 撞上進行中的 transaction,只 commit 一半還不報錯
- 作者:u/Mixon1212 | 57↑ | 42 則留言
- 輔助來源:
- 原文:Hibernate lost my update(Bartosz Osipiuk)
- Hibernate issue:HHH-21038(2026-10-22 提交,作者撰文時狀態為 New)
報導
(本報賈新聞/科技組報導)一位波蘭工程師在 r/java 分享自己踩到的資料遺失問題:Spring Boot 應用收到 SIGTERM 做 graceful shutdown 時,若某個 thread 正卡在 transaction 中間,Hibernate 最後會照常 commit,卻跳過 commit 前的 flush。結果是先前已送出的 INSERT 進了資料庫,還在記憶體裡等 flush 的 UPDATE 卻消失,整個過程不拋任何 exception,只留一行 TRACE 等級的 log。作者是從一筆 dead-letter 事件發現的:事件描述某筆紀錄已刪除,資料庫裡那筆 row 卻原封不動。
依原文的重現情境,一個 @Transactional method 載入訂單、儲存付款紀錄、把訂單狀態改成 PAID,接著呼叫一個很慢的外部服務。這時 SIGTERM 到達,Spring 先停 Kafka/RabbitMQ consumer 與 HTTP endpoint,各自只給有限的等待時間(原文舉 Kafka 10 秒、RabbitMQ 5 秒,每個 phase 最多 30 秒),逾時後就進入 bean 銷毀階段,依序關閉 EntityManagerFactory 與 DataSource。還在跑的 thread 回來 commit 時,SessionImpl.managedFlush() 會檢查 isOpenOrWaitingForAutoClose(),而這個判斷包含 factory.isOpen();factory 一關,所有 session 都被視為已關閉,於是印出「Skipping auto-flush since the session is closed」後直接略過 flush,transaction 卻仍然 commit。其他 session 操作會經過 checkOpen() 而拋錯,唯獨 commit 時的 flush 是默默跳過。
哪些變更會受影響?原文整理得很清楚:entity 欄位修改、remove()、使用 sequence 產生 ID 的新 entity,都會留在記憶體等 flush;IDENTITY 產生 ID 的 insert、JPQL/native DML、直接走 JDBC 的語句,以及已經 flush 過的變更則會立刻送出。這也是「INSERT 有到、UPDATE 沒到」的原因。作者在 Spring Boot 4 + Hibernate 7 + PostgreSQL 的 demo 中,Kafka listener、RabbitMQ listener、HTTP request、@Async、@Scheduled 五種入口全部重現,版本涵蓋 Hibernate 6.6.53、6.6.59、7.4.5、7.4.13,H2 上也重現到 8.0.0.Beta3。實測 factory 關閉到連線池關閉的空窗約 1.2 ms(中位數),落在前半段的 commit 會變成無聲的部分寫入,較晚的則會因 Socket closed 失敗。若是 SIGKILL,連線直接中斷、PostgreSQL 會 rollback,反而不會出事。
追溯根源,2013 年的 HHH-8586 修正把 factory 關閉後的 flush 從「拋錯」改成「略過」,作者認為當時的測試剛好沒有待寫入的資料,才沒發現會掉資料,屬於疏忽而非刻意設計。他已提交 HHH-21038,建議 session 仍有待執行動作時 managedFlush() 應該拋錯,退而求其次也該把 log 提升到 WARN 或 ERROR。
在官方修正之前,作者的修法是一個 PartialCommitGuard:實作 Spring 的 TransactionExecutionListener,在每個 transaction 開始時對 Hibernate transaction 註冊一個 Synchronization,於 beforeCompletion()(flush 判斷之後、Connection.commit() 之前)檢查 factory 是否已關閉,若已關閉且不是唯讀 transaction(以 FlushMode.MANUAL 判斷)就拋例外,讓 transaction rollback,交給訊息重送或呼叫端重試。以下依原文片段整理骨架(非完整程式碼):
@Component
public class PartialCommitGuard implements TransactionExecutionListener {
// afterBegin:對每個 EntityManagerHolder 取得 session 後
// session.getTransaction().registerSynchronization(new Guard(session));
}
// Guard.beforeCompletion()
if (session.getSessionFactory().isClosed() && !readOnly()) {
throw new HibernateClosedBeforeCommitException();
}
Spring Boot 會自動掛上 TransactionExecutionListener 型別的 bean,所以宣告一個 @Component 即可。作者在 Reddit 貼文中表示這個修法已在每秒 8 萬筆以上 transaction 的正式環境跑過;原文的測試則顯示,在 commit 前後不同時間點關閉 factory 跑 1000 次,沒有 guard 時有 2〜4% 變成部分寫入,加上 guard 後為零。代價是:若 factory 恰好在一次完整 flush 之後才關閉,guard 會把原本沒問題的 transaction 也 rollback;JTA 與手動建立的 entity manager 也不在涵蓋範圍內。
想檢查自己系統的讀者,可以依原文建議逐項確認:(1) 在 metrics 中量測 transaction 時間,長 transaction 是高風險對象;(2) 把外部 API 呼叫、服務間 HTTP 呼叫與耗時操作移出 transaction,移不出來就設定比 shutdown 等待時間更短的 timeout,例如 spring.http.clients.read-timeout(原文提醒 Spring Boot 4.1 的 HTTP client 沒有預設 read timeout,@Transactional 也沒有預設 timeout);(3) 加上 guard;(4) 追蹤 HHH-21038,官方修好後再移除。(本報補充,非原文內容)維運端也可以留意:部署時若 pod 的 graceful shutdown 等待時間比最長的 transaction 還短,就是這個問題的溫床。
本報觀點:這不是每天都會發生的事,但發生時完全無聲,屬於最難追查的那一類。與其爭論誰該負責,不如先把「transaction 裡打外部 API」這個老毛病處理掉,那才是讓空窗出現的根本原因。 ← 藏鏡人批:transaction 裡打外部 API,平常是效能問題,shutdown 時就變成資料問題。
社群反應
| 觀點 | 說明 | 代表留言 |
|---|---|---|
| 使用方式問題 | 認為 transaction 本身有正常 commit,問題在 schema 沒把業務規則擋在資料庫層 | 「交易本身是正常的,也確實 commit 了。出乎意料的是 Java 物件的變更沒有被轉成 SQL。如果你不希望某筆資料在欄位不齊時被寫入,那是資料庫 schema 該修的問題。」(17↑) |
| 依賴 transaction 很合理 | 多數人反駁上述說法,認為原子性正是 transaction 存在的意義 | 「依賴 transaction 本來就是合理的做法。」(52↑) |
| 這就是 bug | 認為在任何情況下都不該跳過 flush 還照樣 commit | 「我絕對會把這歸類為 bug。Hibernate 無論如何都不該略過 flush 還試圖 commit,這破壞了 unit of work。」(5↑) |
| 文件早有警告 | 指出 Hibernate 文件寫明 factory 關閉後不應再使用 session,Hibernate 也不是 thread-safe | 「也許我漏看了什麼,但你的範例做的正是文件說不該做的事:關閉 session factory 之後還繼續用 session。」(1↑) |
| 乾脆不用 ORM | 有人藉機呼籲捨棄 JPA/ORM,改用原生 JDBC | 「別用 JPA。」(15↑) |
| 文章寫法 | 有讀者認為原文篇幅過長,建議開頭就講清楚是 Hibernate 層的 transaction 而非 SQL transaction | 「我覺得你應該在文章開頭就強調,你講的是 Hibernate 的 transaction,不是 SQL 的 transaction。Hibernate 是在 flush 時跳過了 update,所以它沒被納入 SQL transaction。」(2↑) |
4. [工具] 只改 javadoc 也要重建 Guava 12 分鐘?Byte Buddy 作者推自製 build tool Jenesis,Gradle 使用者拿實測數據反駁
- 作者:u/raphw | 26↑ | 19 則留言
- 輔助來源:
- 作者比較頁:Jenesis — Why this tool
報導
(本報賈新聞/工具組報導)以「Byte Buddy 那個人」自稱的 Rafael Winterhalter(u/raphw),在 r/java 公開他近期投入的 Java build tool「Jenesis」。他讓 coding agent 把八個 Java 開發者幾乎天天在用的 library(Guava、JUnit 5、Mockito、AssertJ、Google Auto、Jackson Databind、Gson、jsoup)改成用 Jenesis 建置,再跟各專案原本的 Maven 或 Gradle build 對照。貼文標題直接點名 Guava:只改一行 javadoc,Maven 也要重跑十幾分鐘。
依比較頁的數據,在不開 daemon 的情境下,Guava 原本的 Maven build 從頭建置要 13 分 30 秒,「什麼都沒改」再跑一次仍要 12 分 50 秒;Jenesis 對應的數字是 11 分 25 秒與 4 秒。頁面整理的中位數是:full build 省 15%,什麼都沒改省 93%,改一個 method 省 24%。數據來自一台四核心機器、取三次中位數,作者在貼文與頁面上都承認這份 benchmark「有缺陷」。
raphw 對兩大既有工具的批評很直接:Maven 的 incremental build 效率不好;Gradle 則是 API 一直 breaking、整體太臃腫,所以他從來沒投入。他在貼文中說,自己「請 Claude 算了行數」:Maven 加上必要 plugin 約 50 萬行,Gradle 約 340 萬行,Jenesis 只有約 5 萬行。需要說明的是,這組行數是作者請 AI 統計的結果,不是第三方量測;比較頁上 Maven 的數字寫的是約 54 萬行(Maven 3.9 加 plugin),與貼文略有出入。
設計上,Jenesis 直接讀 module-info.java 取得相依關係,用 javadoc tag 標註 Java 版本與 main class 等 metadata,也能照 Maven 的方式建置既有的 pom.xml。另一個重點是供應鏈:pin 指令會記錄每個相依套件的版本與 SHA-256 checksum,可設定成缺 checksum 或簽章就報錯,jar、POM 與 SBOM 在 clean build 之間保證逐位元組相同。
這份 benchmark 最大的爭議在「拿什麼比」。Caffeine 作者 NovaX 指出 Google 的專案不是好例子:Google 內部用 Blaze 建置,對外的 OSS build 只做到「當個好公民」的程度。他貼出 Caffeine 調校過的 Gradle build(含 remote build cache)數據:clean 的 javadoc build 4 秒、什麼都沒改 1 秒、改一個 method 36 秒,並建議拿這種維護良好的 build 當對照組。維護私有 Guava fork 的 javaprof 更直接,質疑 Gradle 根本沒開 configuration cache:他的 fork 從 Maven 換成 Gradle 之後,clean build(略過測試)19.4 秒、沒改動 0.8 秒、改一個 method 7.4 秒。
raphw 的回應是,他要量的是「工具出廠時的樣子」,不是各專案能調校到多好。截圖是 cold build,頁面另有 daemon 與 warm cache 的分頁,Maven 那邊也換成 mvnd 比較。他強調 cold 數字模擬的是「agent 在 VM 或雲端映像檔裡從頭 clone、從頭建置」,這種情境正越來越常見;Guava 的測試很重,19 秒的數字不含測試,自然不能直接比。他也承認 Gradle「很多地方做得對,但你不能犯錯」,不少 plugin 會弄壞 configuration cache,而且 154 MB 的 Gradle 必須先熱起來才會快。
另有讀者問到 Mill。raphw 事後補上 commons-io 的 Mill 對照:warm build 三次中位數,Jenesis 71 秒、Mill 73 秒,速度相當,cold build 也差不多。
本報觀點:這場爭論的本質是 cold build 對 warm build、出廠設定對調校設定,兩邊的數據其實都成立,只是回答的問題不同。Gradle 的使用者證明了「調校得好可以很快」,raphw 則主張「不調校也應該要快」。在 agent 動不動就開一台乾淨 VM 從頭建置的時代,後者的訴求確實比過去更有份量;但在拿出跟調校過的 Gradle build 正面對照的數據之前,「比 Gradle 快」這個結論還言之過早。 ← 藏鏡人批:benchmark 要先講清楚在比什麼,不然兩邊都對、也都沒說服對方。
社群反應
| 觀點 | 說明 | 代表留言 |
|---|---|---|
| 調校過的 Gradle 並不慢 | Caffeine 作者建議改拿維護良好的 build 當對照組 | 「Caffeine 有成熟的 Gradle build,還有 remote build cache……也許這會是個好的測試平台,可以看出一個維護良好的 build 下,Jenesis 會贏在哪、輸在哪。」(3↑) |
| 質疑沒開 configuration cache | 私有 Guava fork 換成 Gradle 後,改一個 method 只要 7.4 秒 | 「我維護私有的 Guava fork,這是用 Gradle 加 daemon 的時間(我們從 Maven 轉過來,因為 Maven 太慢)……這才是工具真實的生產力。」(4↑) |
| 受夠 Maven 的改進速度 | 期待有經驗、會長期投入的人做的 build tool | 「如果你做得出來,我會大力推廣。我受夠了沒有經驗豐富、長期投入的人在背後支撐的 build 系統……就算送 PR 也常常石沉大海。」(1↑) |
| 供應鏈與 SBOM 是賣點 | 追蹤 SHA 對稽核有幫助,可能成為轉換的理由 | 「第一眼看到我就很喜歡它會追蹤 SHA。在 SBOM 越來越常被稽核要求的今天,這是很漂亮的功能。」(2↑) |
| 安全性要求 | 對作者自建的 module 轉址服務提出基本要求 | 「先不談你的轉址服務值不值得信任,至少要拒絕 port 80 的明文 HTTP,只在加密連線上提供轉址。」(2↑) |
| 行數本身就是賣點 | 對 5 萬行的精簡規模買單 | 「『Jenesis 用 5 萬行做到同樣的事』,聽起來真是悅耳。」(1↑) |
5. [社會] 又一個 logging 框架?SLB4J 發表文 0 分、80 則留言,社群先問 CVE 再問差在哪
- 作者:u/idontlikegudeg | 0↑ | 80 則留言
報導
(本報賈新聞/社會組報導)r/java 本週出現一篇新 logging 專案的發表文:SLB4J,Apache License 2.0 授權,作者 u/idontlikegudeg 的定位是「只做 backend、不提供新的 logging API」。一份設定檔同時接住 Log4J2 API、SLF4J、JUL、JCL 四種 frontend,省掉各種 binding 與 bridge,設定沿用相容 Log4J2 的 log4j2.properties,core jar 約 211 KB、沒有外部 runtime 相依,版本仍是 0.x。結果貼文最後停在 0 分、upvote 比例 0.43,留言卻衝到 80 則。這場討論的重點其實不在 SLB4J 本身,而是 Java 社群看到「又一個 logging 框架」時的直覺反應。
得票最高的留言來自 u/emberko,用反諷開場:「拜託多做幾個 log backend,現有那五、六個的 CVE 還不夠多。」經歷過 Log4Shell 的 Java 開發者,對 logging 元件的資安疲勞已經成了反射動作;多一套實作,就多一個要追蹤、要 patch 的供應鏈節點。第二波質疑是定位。u/CptGia 直接問「為什麼要用這個而不是 Logback」,u/sadbuttrueasfuck 說大家繞了一圈,「做出 facade 的 facade」。u/javaprof 則把 SLF4J 的作法一步步列出來:在 Gradle 用一行 exclude 拿掉所有 backend,再加 adapter 與 backend 就好。他認為 SLB4J 只省下其中一步,換來的卻是沒經過實戰考驗的實作。
命名也被點名。u/user_of_the_week 認為「slb4j」這個名字暗示和 slf4j 關係密切,實際上兩者毫無關係;u/juvenislux 則開玩笑說乾脆叫 All4j(Another Logging Library for Java)。接著有人翻進 repo:u/chabala 指出 build.gradle.kts 裡有八、九成是自訂 build 程式碼,u/snugar_i 補一句「用 regex 解析 TOML,會出什麼錯呢?」。u/agentoutlier 說自己「沒靠任何工具」看了幾分鐘,就在 JsonLayout 找到 thread name 沒做 JSON escape、還漏寫 logger name 的 bug,結論是完全不能上 production。
作者的回應倒是讓這串討論沒有失控。他幾乎每則都回,語氣客氣,反覆說明 SLB4J 是取代 backend、不是再包一層 facade。他承認貼文寫得不清楚,也承認命名有問題,「說得有道理,也許該問問 Ceki(SLF4J 作者)怎麼看」。被抓到 bug,他回「所以才是 0.x,謝謝回報,其他發現也請分享」。至於 build script,他解釋 TOML 只在 build 時使用、不會進到發布的 artifact,自訂程式碼是為了 selective publishing,減少推上 Maven Central 的 artifact 數量。
最耐人尋味的是 vibe code 這段。agentoutlier 在深層回覆推薦自己開發的 logging backend Rainbow Gum,說那是「不是週末 vibe code 出來的」,還問作者「沒看到我的專案嗎?」。作者回應專案的 initial commit 在 11 個月前,「不是 vibecoded」。agentoutlier 隨後改口,說那段話其實是針對其他類似貼文,不是針對 SLB4J,並為語氣道歉,解釋自己原本預計這週發 1.0,網路效應很難對抗,所以特別激動。他還坦承:「我現在也用 LLM 了」,專案起初沒用,最近半年放棄堅持。作者則回「用 LLM 本身沒有錯,但至少要看得懂生成的程式碼」,並說他曾讓 LLM 做資安檢查,同樣沒抓到那個 escape bug。
本報觀點:這篇貼文的分數反映的與其說是 SLB4J 的品質,不如說是 Java 社群對 logging 元件的戒心。依賴越多,攻擊面越大,這個顧慮站得住腳;但作者沒有防衛、願意收 bug、清楚標示 0.x,這個態度值得肯定。至於 vibe code 的指控,結果是質疑方與被質疑方都承認用 LLM,真正的分界不在用不用 AI,而在誰願意為生成的程式碼負責。 ← 藏鏡人批:logging 元件出過一次 Log4Shell,社群的戒心就再也放不下了。
社群反應
| 觀點 | 說明 | 代表留言 |
|---|---|---|
| CVE 疲勞 | 多一套 logging 實作就多一份供應鏈風險 | 「拜託多做幾個 log backend,現有那五、六個的 CVE 還不夠多。」(48↑) |
| 定位不清 | 被誤認為 facade 的再一層包裝 | 「我們繞了一圈,做出 facade 的 facade。」(15↑) |
| SLF4J 就做得到 | exclude 加 adapter 即可,不必換上沒經過考驗的實作 | 「用 SLF4J:一行 exclude 拿掉所有 backend,加上 adapter,再加需要的 backend。請用穩定、高效、經過測試的實作。」(13↑) |
| 名字太像 | 命名暗示和 SLF4J 有關係 | 「我不覺得取一個暗示和 slf4j 關係密切、實際上卻毫無關係的名字是好主意。」(10↑) |
| 程式品質 | build script 自訂碼過多,JSON 輸出有 escape bug | 「用 regex 解析 TOML,會出什麼錯呢?」(3↑) |
| 作者回應 | 承認 bug,以 0.x 定位,歡迎回報 | 「所以才是 0.x。謝謝回報,其他發現也請分享。」(2↑) |
6. [產業] CheerpJ 5.0 登場:瀏覽器裡跑 Java 21/25 與完整 JDK,連 javac 都能在前端編譯
- 作者:u/alexp_lt | 76↑ | 26 則留言
- 輔助來源:
報導
(本報賈新聞/產業組報導)專門把 Java 搬進瀏覽器的 CheerpJ 推出 5.0 版。發文者 u/alexp_lt 在貼文開頭就揭露自己任職於開發商 Leaning Technologies,因此這是一則廠商發表自家產品的公告,以下功能說明都來自官方部落格,並非第三方評測。
這次的重點是版本補齊。CheerpJ 原本已支援 Java 8、11、17,5.0 加入 Java 21 與 25,完成 2025 年 2 月公布的路線圖:在 2026 年底前追上最新的 Java LTS。官方特別說明 Java 25 目前仍屬 preview。另一項改變是每個支援版本都改為附上完整 JDK,不再只有 JRE,所以 javac 與 jshell 也能直接在瀏覽器端執行,官方主打的是教學用途。Java 11 與 17 則依社群與客戶的測試回饋宣告穩定。
另一個賣點是實驗性的 WasmGC 引擎。CheerpJ 預設引擎的做法是把 bytecode JIT 編譯成 JavaScript;新引擎則把 Java 物件對應成 WebAssembly GC 物件,熱點程式碼編譯成 WebAssembly,記憶體仍交給瀏覽器的 garbage collector 管理。開啟方式是 cheerpjInit({ wasmGC: true }),需要 Chrome/Edge 137、Firefox 153、Safari 27 以上。官方自己也說得很保守:這個引擎目前不穩定、還沒有 fallback 機制,不預期真實應用能正常運作,也不建議用在 production。官方宣稱啟動與執行速度都會「大幅提升」,但沒有附任何 benchmark 數字;預計要到 2027 年初的 CheerpJ 6.0 才會達到 production 等級,在那之前預設引擎不會換。
其他改進包括:預設引擎的 class file 解析與 JIT 重新調校、JAR 下載最佳化(同樣沒有數字);Swing/AWT 支援自訂 Look & Feel、多視窗、剪貼簿與列印,但 JavaFX 與 SWT 仍未支援;Library Mode 可以從 JavaScript 用 async/await 呼叫 Java 函式庫,官方範例是用 iText 在前端產生 PDF。展示案例有直接執行 Mojang 原版 Minecraft client.jar 的 Browsercraft,以及在瀏覽器裡編譯現代 Java 的 BrowserCode。後續規劃則有 NPM 套件(目前只能從 CDN 載入)、Java 25 轉為穩定,以及「新 LTS 釋出後最多延遲六個月」就跟上支援。
授權方面,官方公告只列出分類、沒有列價格:個人、教育、興趣用途、技術評估、個人與一人公司,以及非商業開源專案可使用免費的 Community license;商業使用分成 Desktop(依組織內部使用的 Java 應用計價)與 Application(自己開發給客戶或公眾使用的軟體)兩種授權,Enterprise 授權則允許自行架設與再散布。討論串裡有網友提到「每位使用者每月 6 英鎊」,這個數字不是出自公告,本報未能查證;alexp_lt 回應時說明按使用者計價的方案適用於組織內部或對外執行的應用,多半是企業網路裡的老舊業務系統,作為開發工具則另有計價,並強調 FOSS 專案、個人與一人公司開發的商業閉源軟體都可以免費使用。
本報觀點:從官方列出的展示與授權結構來看,CheerpJ 最清楚的定位仍是讓 Swing 與 Applet 時代的企業舊系統在不改程式碼、不必安裝 JRE 的前提下繼續運作,教學用的瀏覽器端 javac 是新增的第二條路線。至於大家最好奇的 WasmGC 效能,在官方拿出數字、6.0 正式推出之前,都還只能算期貨。 ← 藏鏡人批:老系統不改程式碼就能上瀏覽器,這才是真正有人付錢的需求。
社群反應
| 觀點 | 說明 | 代表留言 |
|---|---|---|
| 真正實現一次撰寫到處執行 | 有開發者用 CheerpJ 把自己的 Java IDE 搬上網頁 | 「我是 CheerpJ 的忠實粉絲,這是一項驚人的工程成果,真的讓『一次撰寫,到處執行』成真。我花了大量心血做的作品如果沒有瀏覽器支援,幾乎就沒有意義。」(11↑) |
| 授權模式限制應用面 | 有人認為以此價格,新前端框架很難採用,比較適合救老系統 | 「每位使用者每月 6 英鎊?拿來維持老舊系統運作也許值得,但我想不到其他用途。不過我猜那就是他們的客群,而且開發成本一定不低。」(3↑) |
| 與 TeaVM 的技術差異 | 網友以為和 TeaVM 一樣把 bytecode 轉成 Wasm,官方澄清 | 「大致正確但不完全。預設引擎下 CheerpJ 是把 bytecode JIT 編譯成 JavaScript,WebAssembly 只會在 CheerpJ 5 開啟、但還不穩定的實驗性 WasmGC 引擎中產生。」(2↑,官方員工回覆) |
| 像是 Applet 復活 | 有人想拿來救回舊的 Applet,官方補充不只是 Applet | 「所以基本上就是 Applet 再現,只是換成 Wasm,而且使用者不必安裝 Java?很酷。我在想能不能把我那些舊 Applet 救活。」(3↑) |
| 其他語言生態也感興趣 | JRuby 維護者表示將測試並改善相容性 | 「我們有一位 JRuby 使用者正在用 CheerpJ 做一些有趣的實驗,這個版本終於推出,他們一定很興奮。我也會自己測試看看,研究 JRuby 能怎麼改進才能在 CheerpJ 上跑得更好。」(7↑) |
| 進階 API 支援程度 | 有人問 FFM API,官方表示目前只支援編成 WebAssembly 的 JNI 模組 | 「目前 CheerpJ 支援編譯成 WebAssembly 的 JNI C/C++ 模組,我們的 Minecraft 展示就是用這種方式跑 LWJGL。我們認為同樣的做法最終也能用在你提到的新 FFI API 上,但目前還不支援。」(2↑,官方員工回覆) |
社群溫度計
| 熱度 | 標題 | 一句話 |
|---|---|---|
| 18↑ | Haze 2.1:防碎玻璃 | Chris Banes 的 Compose 模糊/毛玻璃效果 library 發布 2.1 版。 |
| 11↑ | TubeHaus:不用 Google 登入的 YouTube client | 訂閱與歷史存在裝置或自架 server,從 Android app 擴成一套 Kotlin codebase、五個平台的 Compose Multiplatform 專案。 |
| 8↑ | 兩年後回看:Kotlin typed domain errors 我做錯了什麼 | 回顧用型別表達領域錯誤的設計取捨,哪些該改、哪些值得保留。 |
| 7↑ | 自動找出 @Scheduled 與 Quartz job,停跑就告警的 starter | 排程默默不跑的痛點是真的,但本質是自家 heartbeat 服務的推廣,需要 API key。 |
| 1↑ | 用 kotlinx.serialization 給人和 AI agent 清楚的設定錯誤 | ignoreUnknownKeys = false 再加一層驗證,讓 agent 拿到能直接修正的錯誤訊息。 |
| 1↑ | 在 musl 系統上跑 Gradle 的方法 | Alpine 這類 musl 環境跑 Gradle 的實作筆記。 |