hashCode
概述
Java中hashCode也称哈希码,能将对象根据一定算法转换为32位int值。
和equals配套使用,重写的话需要保持原子性,要么都重写,要么都不重写。equals相同的话哈希值一定相同,哈希值不同的话equals不一定相同,存在hash冲突。
hash冲突
-
开放定址法:将对象值hash出现冲突后,根据结果值再次hash,直到结果不冲突
-
链地址法:建立链表,冲突的hash值用链表连接
-
建立公共溢出区:分基本表和溢出区,将冲突的hash值放进溢出区
-
再哈希法:多个hash函数,一个重复换另一个
生成策略
JVM启动参数中,添加-XX:hashCode=值,可以控制hashCode的计算方式。共6中生成策略,值为0,1,2,3,4,其他
0 - 用Park-Miller伪随机数生成器
1 - 地址和随机数异或运算
2 - 总返回常量1
3 - 使用全局递增序列
4 - 对象地址的当前地址
5 - 线程局部状态实现Marsaglia‘s异或-位移随机数生成(有兴趣了解 ==> Marsaglia`s Xorshift Random Number Generators)
其中仅有1,4生成结果和地址有关,JDK8以前默认是1,JDK8之后默认是5
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {//随机数生成
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
} else
if (hashCode == 1) {//这与第5个方式类似,都调用了cast_from_oop函数,只不过此处又增加了位偏移和addrBits ^ (addrBits >> 5) ^ GVars.stwRandom计算
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
} else
if (hashCode == 2) {//灵敏度测试,不知道干嘛的
value = 1 ; // for sensitivity testing
} else
if (hashCode == 3) {//自增序列
value = ++GVars.hcSequence ;
} else
if (hashCode == 4) {//
value = cast_from_oop<intptr_t>(obj) ;
} else {//Marsaglia's 异或-位移方案
// Marsaglia's xor-shift scheme with thread-specific state
// This is probably the best overall implementation -- we'll
// likely make this the default in future releases.
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
}
value &= markOopDesc::hash_mask;
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc::no_hash, "invariant") ;
TEVENT (hashCode: GENERATE) ;
return value;
}
存储位置
对象,保存在JVM堆区域中,此区域线程共享。在HotSpot虚拟机内,对象在堆内存中存储布局分三部分:对象头,实例数据、对齐填充。
其中对象头存放两类数据:运行时数据、类型指针
运行时数据有哈希码、GC分代年龄、锁状态标识、线程持有锁、偏向线程ID、偏向时间戳等。该部分数据在32位和64位JVM上分别为32bit和64bit,官方称为’Mark Word‘。例如32位HotSpot虚拟机中,在对象未被同步锁锁定的情况下,Mark Word 32个比特空间中25个用来存哈希码、4个存分代年龄、2个存锁标识位、1个为0。其他情况如下表:
类型指针即对象指向它类型元数据的指针,JVM通过该指针来判断此对象是哪个类型的实例。但并非所有JVM都要在对象头保留类型指针,查找对象类型不一定非要经过对象本身。此外若对象是个数组,则头中还要有个数据记录数组长度。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)