Question Deux instances de classe différentes donnant le même hashCode


Je rencontre un problème étrange sur un serveur JBoss où deux classes produisent le même 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()));

Produit:

fnq.Class1
fnq.Class2
494722
494722
494722
494722
false
false
true

Normalement, je m'en fous, mais nous utilisons un framework qui met en cache les paramètres en utilisant une clé composée de codes de hachage de la classe et d'un nom de propriété. C'est une mauvaise conception pour la mise en cache, mais pour le moment, elle est hors de mon contrôle (OGNL 3.0.6 dans les derniers Struts 2.3.24, voir la source. Un OGNL plus récent résout le problème, mais il ne sera pas dans Struts avant la version 2.5, actuellement en version bêta.)

Ce qui me rend un peu bizarre, c'est

  • Le problème apparaît après plusieurs jours d'utilisation ... et je suis presque sûr que les deux classes / propriétés sont mises en cache pendant ce temps. Cela m'amène à croire que le hashcode de l'instance de classe est en fait en changeant... ils sont devenus égaux après plusieurs jours.
  • Nous avons observé le comportement dans un Hotspot 1.6 très obsolète, et maintenant sur 1.7.0_80. Les deux sont des versions 32 bits sur Sun Sparc
  • Rapports JVM -XX: hashCode comme "0"

J'ai lu que le générateur de hashcode RNG dans Hotspot (la stratégie "0") peut produire des doublons s'il y a des threads de course, mais je ne peux pas imaginer le chargement de classes déclenchant ce comportement.

Hotspot utilise-t-il une gestion de hashcode spéciale lors de la création d'un Class exemple?


10
2017-09-02 03:26


origine


Réponses:


  1. java.lang.Class ne remplace pas hashCode, ni JVM ne le gère en particulier. Ceci est juste le hashCode d'identité habituel hérité de java.lang.Object.
  2. Quand -XX:hashCode=0 (par défaut dans JDK 6 et JDK 7), le hashCode d'identité est calculé à l'aide du générateur de nombres aléatoires global de Park-Miller. Cet algorithme produit des entiers uniques avec la période de 2^31-2, il n'y a donc presque aucune chance que deux objets aient le même hashCode, sauf pour la raison ci-dessous.
  3. Comme cet algorithme repose sur une variable globale non synchronisée, il existe en effet une possibilité que deux threads différents génèrent le même nombre aléatoire en raison de la condition de concurrence. (la source). C'est ce qui se passe apparemment dans votre cas.
  4. Le hashCode d'identité n'est pas généré lors de la création de l'objet, mais lors du premier appel à hashCode méthode. Donc peu importe quand et comment les classes sont chargées. Le problème peut arriver avec deux objets si hashCode est appelé en même temps.
  5. Je suggère d'utiliser -XX:hashCode=5 (par défaut dans JDK 8). Cette option utilise le RNG Xorshift local au thread. Il n'est pas soumis aux conditions de course et est également plus rapide que l'algorithme de Park-Miller.

5
2017-09-03 13:21