JVM 如何优化 31 * x 为 (x << 5) - x?

前言:

在 Java 的 hashCode() 计算中,经常会看到 31 * x 这样的乘法运算。JVM 会将其优化为更高效的位运算形式 (x << 5) - x,以提高计算速度。下面详细解释这一优化原理:


1. 为什么选择 31?​
  • 31 是一个质数,能减少哈希冲突(质数在取模运算时分布更均匀)。
  • 31 的二进制形式是 11111**(即 2⁵ - 1 = 31),这使得它特别适合用移位和减法优化。

2. 数学等价性

31 * x 可以拆解为:

java 复制代码
31 * x = (32 - 1) * x = 32 * x - x

32 * x 在二进制中相当于 x 左移 5 位(因为 32 = 2⁵):

java 复制代码
32 * x = x << 5

因此:

java 复制代码
31 * x = (x << 5) - x

3. 为什么位运算更快?​
  • 乘法运算(*)​:在 CPU 上可能需要多个时钟周期(通常为3~4个CPU周期),尤其是早期处理器。
  • 移位(<<)和减法(-)​:是单周期指令(各需要一个周期,组合后仅需要2个周期),执行速度更快。

JVM(特别是 HotSpot)会在运行时自动将 31 * x 替换为 (x << 5) - x,从而提升 hashCode() 的计算效率。


4. 验证优化

可以通过查看 JVM 生成的汇编代码(使用 -XX:+PrintAssembly)来确认这一优化。例如:

java 复制代码
public int hash31(int x) {
    return 31 * x;
}

JIT 编译器会将其优化为:

java 复制代码
mov    eax, x      ; 加载 x 到寄存器
shl    eax, 5      ; x << 5
sub    eax, x      ; eax = eax - x

5. 其他类似优化

JVM 对某些固定乘数(如 3, 7, 15, 31, 63 等 2ⁿ -1 形式的数)会采用类似的优化:

  • 7 * x(x << 3) - x
  • 15 * x(x << 4) - x
  • 63 * x(x << 6) - x

31 是最常用的,因为它在哈希计算中平衡了性能和冲突率。


总结
  • 优化原理31 * x = (x << 5) - x,利用二进制特性转换为高效位运算。
  • 性能提升:移位和减法比直接乘法更快,JVM 自动应用此优化。
  • 适用场景 :适用于 hashCode() 计算、字符串哈希等需要快速乘法的场景。

这种优化是 JVM 对常见模式的智能编译策略之一,开发者无需手动改写,直接使用 31 * x 即可获得最佳性能。

相关推荐
野犬寒鸦36 分钟前
力扣hot100:相交链表与反转链表详细思路讲解(160,206)
java·数据结构·后端·算法·leetcode
ytadpole1 小时前
揭秘设计模式:工厂模式的五级进化之路
java·设计模式
计算机毕业设计木哥1 小时前
计算机毕设选题:基于Python+Django的B站数据分析系统的设计与实现【源码+文档+调试】
java·开发语言·后端·python·spark·django·课程设计
失散131 小时前
分布式专题——1.2 Redis7核心数据结构
java·数据结构·redis·分布式·架构
用户3721574261352 小时前
Python 实现 HTML 转 Word 和 PDF
java
a587692 小时前
Java核心概念精讲:TCP与UDP的区别、Java NIO的几个核心组件与HTTP和HTTPS的区别等(46-50)
java·面试·nio
渣哥2 小时前
ConcurrentHashMap 的 get 要不要加锁?一次“多此一举”的心路历程
java
愿你天黑有灯下雨有伞2 小时前
一种基于注解与AOP的Spring Boot接口限流防刷方案
java·spring boot·后端
MuMuMu#2 小时前
JAVA NIO学习笔记基础强化学习总结
java·学习·nio
拾忆,想起2 小时前
Redis复制延迟全解析:从毫秒到秒级的优化实战指南
java·开发语言·数据库·redis·后端·缓存·性能优化