JavaProgrammingSenior Java Developer

How does the **HotSpot JVM** ensure that **Object.hashCode()** returns consistent values after the garbage collector relocates the object to a different heap address, despite the identity hash being initially derived from the object's original memory location?

Pass interviews with Hintsage AI assistant

Answer to the question.

The HotSpot JVM guarantees Object.hashCode() consistency across object relocation by computing the value once—typically from the initial memory address—and caching it in the object header's mark word before any garbage collection cycle can move the object. This mark word contains a dedicated hash code field alongside a bit flag indicating that the hash has been materialized, ensuring subsequent invocations retrieve the cached value rather than recalculating. Consequently, even when collectors like G1 or ZGC evacuate the object to a new address, the identity hash remains stable because it is detached from the physical pointer and stored in the immutable header metadata.

Situation from life

A distributed web application utilized IdentityHashMap to track active Session objects across multiple application nodes, relying on System.identityHashCode() for cache affinity routing during load balancing operations. During peak traffic, the ZGC low-latency collector performed frequent concurrent relocations of young generation objects to maintain tight pause-time targets. If the identity hash had altered upon movement, session affinity would break, causing requests to bleed across nodes and violate consistency guarantees.

One approach involved generating UUID instances for each Session upon creation and maintaining a separate ConcurrentHashMap<UUID, Session>. Pros: Complete independence from JVM object lifecycle and relocation mechanics. Cons: Adds sixteen bytes of overhead per session object and introduces allocation pressure from UUID generation, potentially saturating the allocation rate during burst traffic.

The team considered pinning session objects in memory using JNI critical references to prevent GC relocation. Pros: Guarantees stable memory addresses and thus stable identity hashes derived from addresses. Cons: Pins entire heap regions in ZGC, causing fragmentation and defeating the collector's concurrent relocation capabilities, leading to unacceptable pause times.

The chosen solution leveraged the JVM specification guarantee that identity hash codes remain constant, combined with HotSpot's mark word caching implementation. Pros: Zero additional memory overhead, no allocation cost, and full compatibility with aggressive collectors like ZGC. Cons: Requires trust in JVM implementation details, though codified in the specification.

The application maintained perfect session affinity through millions of ZGC cycles without pinning or auxiliary identifiers, achieving sub-millisecond pause times while preserving IdentityHashMap integrity.

What candidates often miss

Does System.identityHashCode() always return the object's current memory address as an integer?

No. While the initial computation may use the memory address as entropy, the result is immediately stored in the object header and never changes thereafter. This means the returned integer does not reflect the object's current location after GC movement, and developers should not treat it as a pointer or memory address probe.

Can the identity hash code be negative, and how do collections handle this?

Yes, any thirty-two bit integer value is valid, including negative numbers. IdentityHashMap handles negative hashes through masking operations like (h ^ (h >>> 16)) & (length-1), avoiding Math.abs() which fails on Integer.MIN_VALUE due to two's complement overflow.

Is the identity hash code guaranteed to be unique across all objects?

No. The thirty-two bit integer space is smaller than the potential heap address space, so collisions are possible. HotSpot uses a Marsaglia's xor-shift scheme or address-based hashing that distributes values well, but uniqueness is not guaranteed, making IdentityHashMap rely on reference equality for disambiguation, not merely hash codes.