This is the fifth day of my participation in Gwen Challenge
Make it a habit to like it first
Let’s start with the simplest print
System.out.println(new Object());
Copy the code
Outputs the fully qualified class name of the class and a string:
java.lang.Object@6659c656
Copy the code
What comes after the @ sign? Is it hashcode or the memory address of the object? Or something else?
The hashcode behind @ is the hexadecimal hashcode of the object.
Object o = new Object();
int hashcode = o.hashCode();
// toString
System.out.println(o);
// hashcode hexadecimal
System.out.println(Integer.toHexString(hashcode));
// hashcode
System.out.println(hashcode);
// This method also gets the object's hashcode; Unlike Object.hashCode, however, this method ignores the overwritten Hashcode
System.out.println(System.identityHashCode(o));
Copy the code
Output result:
java.lang.Object@6659c656
6659c656
1717159510
1717159510
Copy the code
So how does the object’s Hashcode actually get generated? Is it really the memory address?
This article is based on JAVA 8 HotSpot
HashCode generation logic
The logic for generating hashCode in the JVM is not that simple, and it provides several strategies, each of which produces a different result.
Take a look at the core method of generating hashCode in the OpenJDK source code:
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) {
// 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 = 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 = intptr_t(obj) ;
} else {
// 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;
}
Copy the code
As you can see from the source code, the build policy is controlled by a hashCode global variable that defaults to 5; This variable is defined in a separate header file:
product(intx, hashCode, 5."(Unstable) select hashCode generation algorithm" )
Copy the code
The source code is very clear… (unstable) Select the algorithm generated by hashCode, and the definition here is controlled by the JVM startup parameter, first check the default value:
java -XX:+PrintFlagsFinal -version | grep hashCode
intx hashCode = 5 {product}
openjdk version "1.8.0 comes with _282"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8. 0 _282-b08)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.282-b08, mixed mode)
Copy the code
So we can configure different hashCode generation algorithms by JVM startup parameters, and test the results under different algorithms:
-XX:hashCode=N
Copy the code
Now let’s look at the different representations of each hashCode generation algorithm.
The 0th algorithm
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(a); }Copy the code
This generation algorithm uses a park-Miller RNG random number generation strategy. But it’s important to note that… This random algorithm will have spin wait at high concurrency
The first algorithm
if (hashCode == 1) {
// 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 = intptr_t(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
}
Copy the code
This algorithm, which is really the memory address of the object, gets the object’s intPTR_T pointer directly
The second algorithm
if (hashCode == 2) {
value = 1 ; // for sensitivity testing
}
Copy the code
I don’t need to explain this… Fixed return 1, should be used for internal test scenarios.
If you’re interested, try -xx :hashCode=2 to turn on the algorithm and see if the hashCode results are all 1’s.
The third algorithm
if (hashCode == 3) {
value = ++GVars.hcSequence ;
}
Copy the code
This algorithm is also very simple, increment, all object hashCode uses this one increment variable. Let’s try it out:
System.out.println(new Object());
System.out.println(new Object());
System.out.println(new Object());
System.out.println(new Object());
System.out.println(new Object());
System.out.println(new Object());
//output
java.lang.Object@144
java.lang.Object@145
java.lang.Object@146
java.lang.Object@147
java.lang.Object@148
java.lang.Object@149
Copy the code
Sure enough, it’s self-increasing… A little meaning
The fourth algorithm
if (hashCode == 4) {
value = intptr_t(obj) ;
}
Copy the code
This is not much different from the first algorithm, which returns the address of the object, but the first algorithm is a variation.
The fifth algorithm
The final, and default, generation algorithm is used when hashCode configuration is not equal to 0/1/2/3/4:
else {
// 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 ; }Copy the code
Here is a hash value from the current value of the XOR (XOR) operation, which is more efficient than the previous increment and random algorithm, but the repetition rate should also be relatively high, but the hashCode repetition rate is also relatively high.
The JVM does not guarantee that the value will not be repeated, as in HashMap, which resolves hash collisions
conclusion
A hashCode can be a memory address or not, and it can even be a constant or increment of 1! It can use any algorithm you want!
Original is not easy, prohibit unauthorized reprint. Like/like/follow my post if it helps you ❤❤❤❤❤❤