变量名越怪,JVM 越快?
在软件工程的共识里,变量命名越清晰越好——意图明确、语义完整、见名知意,这能降低沟通成本、减少误解、提升可维护性。几乎所有风格指南都把“有意义的命名”视为第一原则。
但今天读到的一篇文章《Java Performs Better When You Misspell Variable Names》,把这条铁律里的“性能部分”掀了桌:在 Java 的某些栈中,刻意缩短、甚至“错拼”的变量名,可能真的让服务更快。不是业务逻辑的变化,而是更短、更“随机”的名字在字符串常量池、哈希和反射路径上更省。在作者的压测里,吞吐提升最高接近 49%。这听起来反常识,但他用微基准、压测与分析器把它变成了一个严肃命题。
这事是怎么被发现的
故事开始于一次“事故”。作者重构时不小心把 customerEmail、orderHistory、totalAmount 之类的变量写成了 custEmil、ordrHstry、totlAmnt。
// 本来要写:
private String customerEmail;
private List<Order> orderHistory;
private BigDecimal totalAmount;
// 实际上线的是
private String custEmil;
private List<Order> ordrHstry;
private BigDecimal totlAmnt;
结果第二天监控显示:平均延迟从 127ms 直接降到 80ms。作者起初怀疑是缓存偶然命中、网络波动或者测量误差,回滚到“整洁命名”,延迟又回到 127ms。这一来一回,逼着他把这件事当真。
于是他系统化地做了验证。用 JMH 写对照实验,两个版本代码逻辑完全一致,唯一变量是“命名长度与形态”:一个版本用规范、完整、可读的名字,另一个版本把元音删掉、前缀缩短、偶尔把名字变得更随机。然后是更接近生产的验证:把同样的策略应用到一个 Spring Boot 服务,在 1000 并发、60 秒的 JMeter 压测下对比两版吞吐和延迟。最后用分析器(如 YourKit)去看字符串相关热点到底是不是在下降。
数据与分析:不是“玄学”,而是成本栈里的一段被忽视的路
在微基准里,作者报告仅删除元音就能带来约 26% 的提升;而当名字更短、更“乱”(比如三四个字符的缩写或无意义组合),提升更明显。在压测里,平均响应从 143ms 降到 91ms,吞吐从 6847 req/s 到 10234 req/s,错误率不变。分析器则显示 String.hashCode() 的总耗时显著下降(调用次数一样),但短名字的总耗时少了近一秒(按 60 秒窗口)。
为什么可能成立?因为 JVM 的字符串常量池(String Table)是哈希表结构,反射、调试、堆栈、框架内省都会不断地触发对这些字符串的查找和哈希。长且前缀相似的名字更容易发生哈希碰撞,查找链更长,缓存局部性更差,GC 在标记-清除阶段扫描保留这批字符串的成本也更高。JIT 能优化计算,但它优化不掉字符串表、反射和 GC 的固定成本。短且更“随机”的名字,往往有更好的哈希分布,更低的碰撞率,更友好的缓存命中。
这也解释了一个让人不适的现实:在反射密集的栈中(Spring、Hibernate、Jackson 等),名字并非“运行时免费”。在某些路径上,名字的长度与分布会成为可测的成本。
我们该怎么办:命名,不再只是风格问题
知道这个结论之后,我们应该调整命名策略吗?我觉得应该,但只在该用的地方用,并且给它加上清晰的边界。
- 先剖析再动刀:用分析器定位字符串相关热点(例如反射入口、序列化/反序列化、框架内省、
StringTable),确认它们确实在吃掉你的时间。 - 只在热点处调命名:把策略限定在高频反射和序列化的类型、字段、方法上;领域模型和业务规则保持可读性,别把团队协作变成解谜游戏。
- 保守优先、激进试点:
- 保守档:删除明显元音、缩短前缀(
customerEmailAddress → cstmrEmlAdr),目标增益 8–12%。 - 激进档:更积极地缩短并弱化相似前缀(
orderHistoryList → ordrHstryLst),目标增益 18–24%。 - 极端档:三四字符的强缩写(
totalAmountPaid → tAP),增益可能更高,但不建议用于生产的核心业务域。
- 保守档:删除明显元音、缩短前缀(
- 搭配替代方案:能用代码生成/注解处理器替代反射就替代;序列化层选择更高效的实现;必要时微调
-XX:StringTableSize并做对照验证。 - 工程化验证:设置可靠的基准(JIT 预热、固定参数、屏蔽 I/O 干扰),看 p95/p99 与吞吐的变化,再决定是否推广。
思考:数据与教条,谁该让步?
- 如果命名在某些栈里是成本,我们是否应该建立一份“热点命名策略”,像性能预算一样,允许在少数关键路径牺牲一点可读性来换取吞吐?
- 不同 JVM 版本、不同 GC 策略、不同框架组合下,这个效果是否一致?是否可以用工具链(重命名器、lint 规则、基准套件)把它做成可重复的实验?
- 当团队规模变大,命名的“可读性收益”和性能的“吞吐收益”如何折算到同一张成本表上?这是否应该由数据驱动,而不是由风格统一驱动?
小结
这篇文章让我重新审视了一个多年未变的前提:命名只是可读性问题。作者用微基准、压测和分析器把它变成了一个性能问题。在需要极致吞吐的系统里,名字可能不再只是“给人看的”,它也在影响“给机器跑的”。我的答案是:策略性地调整命名,但只在热点路径,并用数据而不是直觉做决定。毕竟,在工程世界里,漂亮的代码不一定是最快的代码,而我们有时需要的,是能顶住流量的那一段真实提升。
变量名越怪,JVM 越快?的更多相关文章
- JVM内存越多,能创建的线程越少,越容易发生java.lang.OutOfMemoryError: unable to create new native thread。
一.认识问题: 首先我们通过下面这个 测试程序 来认识这个问题:运行的环境 (有必要说明一下,不同环境会有不同的结果):32位 Windows XP,Sun JDK 1.6.0_18, eclipse ...
- 如何解决eclipse、MyEclipse中变量名自动补全问题
背景:这个问题困扰了很长时间,解决过程也并不顺利.不断的试错,再次让我理解这下面这句话—— 世界上对的路可能只有一条,错的路却可能有成千上万条,不要成为别人的前车之鉴.开发之路,只需要记住对的路就行了 ...
- 阿里 Java 手册系列教程:为啥强制子类、父类变量名不同?
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 父子类变量名相同会咋样? 为啥强制子类.父类变量名不同? ...
- (编程语言+python+变量名+垃圾回收机制)*知识点
编程语言 从低级到高级的发展的过程 1.机器语言 计算机是基于电工作的.(基于高.低电平 1010010101011) 如果用机器语言表现一个字符的意思需要多段代码的行.但是计算机读取的快. 所以机器 ...
- JavaScript:声明变量名的语法规则
一.语法规则 1.变量必须使用字母.下划线(_)或者美元符($)开始. 2.然后可以使用任意多个英文字母.数字.下划线(_)或者美元符($)组成. 3.不能使用JS关键词与保留字. 二.示例 var ...
- JS中变量名和函数名重名
今天骚凯问了一道变量名冲突的题目,感觉很有意思,顺便也复习一下预解析的一些知识,有不对的地方忘前辈大神指正,题目是这样的: var a=100; function a(){ console.log(a ...
- 使用 v-cloak 防止页面加载时出现 vuejs 的变量名
使用 vuejs 做了一个简单的功能页面,逻辑是,页面加载后获取当前的经纬度,然后通过 ajax 从后台拉取附近的小区列表.但是 bug 出现了,在显示小区列表之前,会闪现小区名对应的 vuejs 变 ...
- C#中根据变量获取变量名字符串
/// <summary> /// 获取当前变量的变量名 字符串 /// 调用:GetVarName(p=>test.str1); 返回 " ...
- C/C++变量名与值的问题
首先说明变量名是不占空间的. 变量:用来标识一块内存空间,这块内存区域的值一般是可以被该的. 而const常量通常限定这一块内存区域的值是不可被更改的. 变量名:只是一个标识符,并不占内存空间.在c的 ...
- JS中变量名作为if条件的 true/flase
在Javascript中,可以直接将变量名放到if条件中, var a;//甚至不定义 if (a){ //... } 以下情况被认为是flase: 1.''空的字符串 2.数字0 3.对象null ...
随机推荐
- Win11纯净版如何修改dns的问题
有很多电脑基地的小伙伴平常在使用电脑的时候会遇到电脑网络无法连接的情况,这时可以尝试通过修改dns地址来解决,不过很多用户不清楚win11纯净版怎么修改dns,其实只要打开网络高级设置就可以了.下面一 ...
- CloudQuery 首次开放API,v1.4.1将开放「部门导入」和「用户导入」
「 API 具有功能丰富.发展迅速且公共可用的特点,极大地推动了以 API 为中心的业务增长.原因有很多,比如 API 随处可用的特性.高效的开发和部署平台,以及摆脱资金密集型需求的金融模型.」 -- ...
- 亚马逊AI模型评估产品评论中的实用建议有效性
产品评论中的实用建议验证模型 电子商务网站的产品评论是消费者购物决策的重要参考,其中常包含"首次使用相机前充电8小时"等非显而易见的实用建议(product tips).为帮助消费 ...
- POSIX 文件系统接口标准
POSIX文件系统接口标准是POSIX(可移植操作系统接口)标准的核心组成部分,旨在为操作系统提供统一的文件操作接口,确保应用程序在不同UNIX及类UNIX系统上实现源代码级可移植性.以下是其 ...
- 微前端:qiankun框架在开发中遇到的问题
基座是Vue3.0 + element plus配合 子应用 Vue2.0 + element ui导致的样式冲突问题 element-plus默认命名空间为el,el会作为其编译后的class名及c ...
- go rock
好久不见呀. happy 314! 阴翳消解的过程就像用小刀裁纸 下一刻不知是会撕裂还是再裁下去 做不到完全沉下 甚至又有什么冉冉升起 这次 再也不是什么新鲜了 对未来充满恐惧 自我怀疑云云 --习惯 ...
- 伏魔挑战赛-ASP/ASP.NET赛道10+绕过样本思路分享
前言 24年年底的时候参加了一下阿里云的第四届伏魔挑战赛,本着学习一下.NET的想法玩的ASP/ASP.NET赛道.但是当时一直没空,等到活动最后一天才抽出时间测试.一个晚上提交了15个绕过样本,重复 ...
- Python面向对象实战之扑克游戏
一.Python面向对象实战之扑克游戏 我们的扑克只有52张牌(没有大小王),游戏需要将 52 张牌发到 4 个玩家的手上, 每个玩家手上有 13 张牌,按照黑桃.红心.草花.方块的顺序和点数从小到大 ...
- [题解]P3952 [NOIP2017 提高组] 时间复杂度
P3952 [NOIP2017 提高组] 时间复杂度 我们把循环的嵌套关系看做树形结构,梳理一下\(3\)种情况: 直接跳过当前子树: \(x,y\in\mathbb{N}\),且\(x>y\) ...
- Docker Desktop里搭建RabbitMq 4.1.3集群的保姆级教程
最近想自己开发一个小系统,而我的电脑里还没有安装任何的开发相关的配套软件和环境,所以想着方便以后开发的原则,就想从头到尾搭建一个RabbitMq cluster集群环境.一开始觉得这个搭建应该不难,网 ...