Java 27 悄悄改了 3 个默认值,我在 1 核小机器上逐个验证了一遍

作者:Flynt日期:2026/9/24

Java 27 发布有一周多了,9 月 15 日 GA。作为非 LTS 版本,很多团队的反应大概是"25 还没捂热,27 与我无关"。这个判断大体没错,生产环境确实应该锚在 25 上。但"不升级"和"没变化"是两回事——27 有三个改动不等你升级就已经定了调子,它们全是默认值:对象头默认压缩、TLS 1.3 默认带上后量子算法、所有环境默认 G1。

我手头正好有台挺"寒酸"的 Linux 小机器,1 个核,不到 2G 内存。这个配置有个特殊意义:从 JDK 9 开始,JVM 的 GC 选择规则是"单核或内存小于 1792MB 就退回 Serial"——我这台机器常年落在这个规则里。所以三个默认值的变化,在我这儿都能直接看出差别。 装的过程没什么可说的,Temurin 两套二进制,25.0.4.1 和 27+35,解压即用。下面按我发现问题的顺序说。

先看对象头:16 字节变 8 字节

紧凑对象头是三个默认值里最"物理"的一个,JEP 534,也是 Project Lilliput落地三部曲的最后一步:JDK 24 实验特性,JDK 25 转正,JDK 27 默认开启。 验证工具我用了 JOL(Java Object Layout),打印一个空对象的内存布局。JDK 25 上是这样:

1java.lang.Object object internals:
2OFF  SZ   TYPE DESCRIPTION             VALUE
3  0   8        (object header: mark)   0x0000000000000001 (non-biasable; age: 0)
4  8   4        (object header: class)  0x001720d8
5 12   4        (object alignment gap)
6Instance size: 16 bytes
7

8 字节 mark word,4 字节压缩类指针,加上 4 字节对齐凑整,一个什么都不装的 Object 占 16 字节。同样的代码放到 27 上:

1java.lang.Object object internals:
2OFF  SZ   TYPE DESCRIPTION             VALUE
3  0   8        (object header: mark)   0x0017740000000009 (Lilliput)
4Instance size: 8 bytes
5

16 字节变 8 字节,对齐损失直接归零。JOL 在 mark word 那行打了个 (Lilliput) 标记——类指针被塞进了 mark word 里,两头并一头。看到自己平时熟悉的 new Object() 被内部项目代号标记出来,还挺有意思的。

不过 JOL 只能说明"单个对象小了",实际收益得看量。我写了个笨办法:固定 640MB 堆,往 List 里疯狂塞 new Object() 直到 OOM,数一下塞了多少个。为了避免 GC 行为差异干扰,两版都显式指定 Serial GC,只留对象头这一个变量:

1JDK 2531,151,587 个对象,每对象 20 字节(8B mark + 4B class指针 + 4B 引用 + 4B 对齐),实占 624MB
2JDK 2731,151,587 个对象,每对象 12 字节(8B mark + 4B 引用),实占 375MB
3

两边倒在同一个位置,对象数一个不差。先解释一下为什么 375MB 就会 OOM:这个实验的死亡触发点不是"堆被对象填满",而是 List 底层那个引用数组的扩容——3100 多万个引用的数组再按 1.5 倍翻一倍,一下子要划出近 300MB 的连续空间。25 那边是 624MB 的对象加上 188MB 的旧数组,真把堆塞满了;27 这边对象只占 375MB,但扩容要的那块连续空间照样给不出来,死在同一个节点上。所以这组数据比的不是"谁先塞满",而是"同样多的对象谁占得少":同样的 640MB,实占从 624MB 降到 375MB,约省 40% 。这个收益对缓存类服务、大集合、对象池这类"堆里全是小对象"的场景最实在。

有一个连带的小坑要提:紧凑头普及后,UseCompressedClassPointers 这个老参数在 JDK 25 就被标记弃用,27 里正式废弃。如果你的启动脚本里还写着它,升级时会直接被拒。我翻 release notes 才注意到这一点,属于"不改代码但改参数"的暗坑。

握手已经悄悄换算法了,前提是两头都是 27

TLS 的默认值变化更隐蔽,也是三个里最容易被忽略的:JEP 527 把后量子混合密钥交换接进了 TLS 1.3,并且默认排在候选组的第一位

背景一句话就够:现在的加密流量可能被截获囤着,等量子计算机成熟后再解密,所以 IETF 搞了混合方案——传统 ECDHE 和 ML-KEM 各算一半,破解其中任何一个都不够。Java 这边是分三步走的:21 给了 KEM API,24 落了 ML-KEM 算法本体,27 把它接进 TLS 并默认启用。27 这一步的变化是:客户端的握手候选列表里,X25519MLKEM768 排到了最前面,什么代码都不用改。 关键问题是:默认开了新算法,老客户端会不会被拒之门外? 我用 JDK 自带的 SSLSocket 写了个几十行的握手探针,自签证书,四种组合各连一遍:

1server=25 client=25  协商 x25519(老组合,无 MLKEM)
2server=27 client=25  协商 x25519(27 服务端对老客户端回落,握手成功)
3server=25 client=27  协商 x25519(27 客户端面对老服务端回落,握手成功)
4server=27 client=27  协商 X25519MLKEM768(后量子生效)
5

四种组合全部握手成功,加密套件都是 TLS_AES_256_GCM_SHA384。27 的客户端日志里能看到它把新组排在了最前面:

1"named groups": [X25519MLKEM768, x25519, secp256r1, secp384r1,
2                 secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096]
3

对面是 27 就用后量子,对面是老的就用 x25519,双向兼容都验证过了,服务端和客户端任何一头先升级都不会炸。如果你显式设置过 jdk.tls.namedGroups 或调用过 SSLParameters.setNamedGroups,那默认列表的变化影响不到你,但也意味着你享受不到——这种情况建议把列表对齐一下。

顺带一提,对比两版日志时我还看到一个不起眼的变化:27 的默认列表把 ffdhe6144ffdhe8192 两个大参数 DHE 组拿掉了,release notes 里对应 JDK-8373426 这条。反正大多数连接走的都是 ECDHE 系,这个变化几乎无感,但你要是真依赖超大 DHE 组,这里会静默少掉选项,属于不查日志根本发现不了的类型。

G1:在这台机器上,它真的慢了

GC 的默认值变化,在我这台 1 核小机器上反应最直白:JEP 523 让 G1 成为所有环境的默认收集器,不再区分 server 和非 server。差异一目了然:

1JDK 25:bool UseSerialGC = true   {ergonomic}
2JDK 27:bool UseG1GC    = true   {ergonomic}
3

两版都是 JVM 自己挑的,规则不同,选择不同。JEP 的理由也充分:这几年 G1 全面进化,JEP 522 砍了同步开销,官方测试显示 G1 在各种堆尺寸上都已经追平 Serial,与其让"单核小内存"偷偷选 Serial,不如统一行为,好理解也好排查。 动机成立,但我还是想看看官方那句"性能不应显著退化"在我这个极端环境下的真实表现。于是把刚才那个塞对象程序改用各自默认 GC 跑了一遍:

1JDK 25 默认(Serial):2.7 秒跑完
2JDK 27 默认(G1)  7.3 秒跑完
3

慢了约 2.6 倍。单核加小堆,正是 G1 区域化管理开销最吃亏的场景。这个负载本身就是极端的分配压力测试,日常业务不会这么极端,但趋势是真实的——JEP 523 的 Risks 一节其实自己写了:受限环境下有些应用用 Serial 仍然最优,这种情况可以显式指定回去。

所以这一段的结论不是"27 的默认变差了",而是:如果你的服务跑在 1 核小容器或者最低配 VPS 上,升级前值得拿真实流量对比一下,不行就加回 -XX:+UseSerialGC,一行参数的事。本地大机器上测不出差别,恰恰是这类默认值变化最阴的地方——它只在你看不见的环境里生效。

写在最后

三个默认值里,对象头和 TLS 基本是白捡的——前者直接给堆省了 40% 的内存,后者把量子时代的路提前铺了一段,都不用改一行代码。G1 那个则更像对"小机器"的重新定价,JVM 不再认为小环境配用最省的收集器——官方赌 G1 已经够好,大多数场景确实,只是我这台 1 核的没赌赢。

生产环境继续锚 25 没问题,但建议把 27 扔进 CI 先跑着,顺便把 UseCompressedClassPointers 这类老参数从启动脚本里清一清。至于这台小机器,我把 G1 留着没动,反正它也不跑什么正经业务——就当替大家踩个雷。


Java 27 悄悄改了 3 个默认值,我在 1 核小机器上逐个验证了一遍》 是转载文章,点击查看原文


相关推荐


09-Rust 测试与质量保证(单元测试 + 集成测试 + Mock + Benchmark + Fuzzing + CI/CD)
yume_sibai2026/9/16

摘要:本文系统讲解 Rust 测试与质量保证体系,涵盖单元测试与集成测试、文档测试、Mock 与测试桩、性能测试(Benchmark)、模糊测试(Fuzzing)、CI/CD 集成等核心内容。每个知识点配有完整代码示例、对比表格、实战场景及常见问题解答,帮助开发者编写健壮的 Rust 代码。 关键词:Rust、测试、单元测试、集成测试、Mock、Benchmark、Fuzzing、CI/CD、质量保证 适合人群:已掌握 Rust 基础的开发者、想提高代码质量的程序员、想建立测试体系的团队


Python 面向对象编程详解:从类与对象到动态属性方法
会飞的拖把2026/9/8

一、前言 在 Python 学习过程中,面向对象编程(Object Oriented Programming,简称 OOP)是一个非常重要的知识点。 Python 不仅支持面向过程编程,也支持面向对象编程。对于简单的小程序,我们可以使用函数快速完成任务;但是当项目规模扩大,代码越来越复杂时,面向对象思想能够帮助我们更好地组织代码,提高程序的复用性和维护性。 本文将详细介绍 Python 面向对象编程中的核心知识: 什么是面向对象类和对象的关系如何创建类和对象self 的作用属性和方法的区别类方法


K8S接入NFS存储
避凉闲庭2026/8/31

一、部署NFS服务端 安装NFS服务 Ubuntu/Debian sudo apt update sudo apt install nfs-kernel-server -y Centos/RHEL / Rocky / AlmaLinux sudo dnf install -y nfs-utils 创建共享目录 sudo mkdir -p 路径及目录名 配置NFS配置文件 sudo vim /etc/exports 增加以下内容 /opt/nfs x.x.x.x/xx(r


node.js
zzx2006__2026/8/23

01_Node.js入门 什么是 Node.js Node.js 是一个独立的 JavaScript 运行环境,能独立执行 JS 代码,因为这个特点,它可以用来编写服务器后端的应用程序Node.js 作用除了编写后端应用程序,也可以对前端代码进行压缩,转译,整合等等,提高前端开发和运行效率Node.js 基于Chrome V8 引擎封装,独立执行 JS 代码,但是语法和浏览器环境的 V8 有所不同,没有 document 和 window 但是都支持 ECMAScript 标准的代码语法Node


Nginx外置缓存-redis2-nginx-module
難釋懷2026/8/10

一、引言:当Lua成为瓶颈,回归C层的必然选择 在《Nginx外置缓存》系列中,我们反复强调OpenResty + lua-resty-redis是生产环境对接Redis的首选方案。这个判断在95%的场景下成立,但剩下的5%恰恰是最极端的性能场景: API网关层纯透传缓存:无需任何业务逻辑,仅需GET/SET+TTL,LuaJIT的协程调度开销占比超过30%;超高频计数器/限流:QPS > 50万,每次请求仅执行INCRBY,Lua层的函数调用和GC压力成为天花板;嵌入式/Nginx精简部署


Go语言第四章(类型转换)
小满zs2026/8/1

类型转换 实际开发里,不同类型之间经常需要互相转换:字符串转数字、数字转字符串、浮点数转整数、整数转浮点数,等等。 数字的转换 比如有一个浮点数 3.1415926535,想把它变成整数,可以用 int() 来转换: package main import "fmt" func main() { num := 3.1415926535 intNum := int(num) fmt.Println(intNum) // 3 } 可以看到,int() 会把浮点数转成整数,并直接舍去小数部分


Python生成器与惰性求值:从yield说起的一场"暂停魔法"
卷无止境2026/7/24

写Python代码写到一定阶段,几乎所有人都会碰到一个绕不过去的坑——处理大数据集的时候,list看着简单顺手,用着用着内存就爆了。这时候有人会甩给你一句话,"用生成器啊"。但生成器到底是什么,yield凭什么能让函数中途停下来又重新跑起来,这背后藏着的其实是Python运行时一套相当精巧的机制。下面就把这套机制拆开揉碎,一步步讲清楚。 🔍 生成器到底是什么 普通函数被调用时,Python会立刻执行函数体,跑到return或者函数末尾就结束,栈帧被销毁,一切归零。生成器函数长得跟普通函数几乎


V040:RAG 检索增强生成的工程链路解构与生产级系统设计
胡萝卜术2026/7/16

V040:RAG 检索增强生成的工程链路解构与生产级系统设计 摘要:本文从工程视角系统解构 RAG(Retrieval-Augmented Generation)的完整技术链路,涵盖文档工程、向量检索原理、相似度评分机制、元数据架构、上下文窗口管理、生产级组件选型及高级检索模式。文章以可观测的中间结果为导向,建立从原型验证到生产部署的技术决策框架,并配套面试应答策略。 引言:RAG 认知的三个层次 对 RAG(Retrieval-Augmented Generation)的理解可分为三个递


AI推理成本降本的三条技术路径:从OpenAI自研芯片到MoE架构的工程化分析
小K讲AI营销2026/7/7

摘要 2026年,AI推理成本已成为制约大模型商业化的核心瓶颈。OpenAI联合博通推出推理专用芯片Jalapeño,推理成本降低50%;DeepSeek通过MoE架构将推理价格压至6元/百万Token,与Claude的181元形成30倍差距。本文从工程化视角分析当前AI推理降本的三条主要技术路径:专用芯片、模型架构优化、系统级调度,并结合实际数据探讨各路径的可行性与局限。 一、推理成本已成为商业化的核心瓶颈 2026年,全球AI算力支出达到500亿美元(数据来源:钛媒体/布罗克森国会证词


MinerU 3.4.0 PDF/文档转 Markdown/Word软件免安装一键启动整合包
2501_946908082026/6/29

一、软件简介 本软件基于 MinerU 3.4.0 开源文档解析引擎,提供了一套开箱即用的图形化文档转换工具。它能够将 PDF、图片、Office 文档(DOCX/PPTX/XLSX)等内容精准地转换为 Markdown 文本或 Word 文档,同时保留原始文档的版面结构和排版信息。下载解压后一键启动即可使用。 二、主要功能特点 1. 多格式输入支持 文件类型格式PDF.pdf图片.jpg, .jpeg, .png, .gif, .webp, .svg, .bmp, .tiff,

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读