I'm encountering a bizarre issue on a JBoss server where two classes are producing the same hashCode()
.
Class<?> cl1 = Class.forName("fqn.Class1");
Class<?> cl2 = Class.forName("fqn.Class2");
out.println(cl1.getCanonicalName());
out.println(cl2.getCanonicalName());
out.println(cl1.hashCode());
out.println(cl2.hashCode());
out.println(System.identityHashCode(cl1));
out.println(System.identityHashCode(cl2));
out.println(cl1 == cl2);
out.println(cl1.equals(cl2));
out.println(cl1.getClassLoader().equals(cl2.getClassLoader()));
Produces:
fnq.Class1
fnq.Class2
494722
494722
494722
494722
false
false
true
I normally wouldn't care, but we're using a framework that caches setters using a key that is comprised of hashcodes from the class and a property name. It's a bad design for caching, but it's beyond my control at the moment (OGNL 3.0.6 in the latest Struts 2.3.24, see source. A newer OGNL fixes the issue, but it won't be in Struts until 2.5, currently in beta.)
What makes the issue somewhat bizarre to me is
I read that the RNG hashcode generator in Hotspot (the "0" strategy) can produce duplicates if there's racing threads, but I can't imagine classloading triggering that behavior.
Does Hotspot use special hashcode handling when creating a Class
instance?
java.lang.Class
does not override hashCode
, nor JVM handles it somehow specially. This is just the regular identity hashCode inherited from java.lang.Object
.-XX:hashCode=0
(default in JDK 6 and JDK 7) the identity hashCode is calculated using global Park-Miller random number generator. This algorithm produces unique integers with the period of 2^31-2
, so there is almost no chance that two objects have the same hashCode except for the reason below.hashCode
method. So it does not matter when and how the classes are loaded. The problem can happen with any two objects if hashCode
is called concurrently.-XX:hashCode=5
(default in JDK 8). This option uses thread-local Xorshift RNG. It is not subject to race conditions and is also faster than Park-Miller algorithm.