2015-09-02 15 views
10

Ich stoße auf ein bizarres Problem auf einem JBoss-Server, wo zwei Klassen dasselbe produzieren hashCode().Zwei verschiedene Klasseninstanzen geben den gleichen 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())); 

Produziert:

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

Ich würde normalerweise nicht egal, aber wir verwenden einen Rahmen, den Setter mit einem Schlüssel-Caches, die von Hashcodes aus der Klasse und einen Eigenschaftsnamen enthalten ist. Es ist ein schlechtes Design für Caching, aber es ist im Moment außer Kontrolle (OGNL 3.0.6 in den neuesten Struts 2.3.24, siehe source. Eine neuere OGNL behebt das Problem, aber es wird nicht in Struts bis 2.5, derzeit sein in der Betaphase.)

Was die Frage etwas seltsam für mich macht, ist

  • Problem nach einigen Tagen der Nutzung erscheint ... und ich bin ziemlich sicher, dass beide Klasse/Eigenschaften während dieser Zeit zwischengespeichert werden, zu werden. Das führt mich zu der Annahme, dass der Klasseninstanz-Hashcode tatsächlich ist, der ändert ... sie wurden nach mehreren Tagen gleich.
  • Wir haben das Verhalten in einem sehr veralteten Hotspot 1.6 und jetzt auf 1.7.0_80 beobachtet. Beide sind 32-Bit auf Sun Sparc baut
  • JVM berichtet -XX: hashCode als "0"

ich, dass der RNG hashcode Generator in Hotspot lesen (die "0" -Strategie) Duplikate erzeugen kann, wenn es Racing Threads, aber ich kann mir nicht vorstellen, dass Classloading dieses Verhalten auslöst.

Verwendet Hotspot beim Erstellen einer Class Instanz eine spezielle Hashcode-Behandlung?

+4

Ich glaube nicht, dass sie sich im Laufe der Zeit ändern werden. Das wäre sehr komisch. Können Sie überprüfen, dass sie nicht gleich am Anfang gleich sind? – Thilo

+0

@Thilo Ich kann nicht 100% ig sein, dass die Hashcodes nicht immer gleich waren. Alles, was ich weiß, ist, dass der Caching-Mechanismus, den ich erwähnte, unter dieser Bedingung sicherlich fehlschlagen würde, wenn er versucht, eine Framework-Action-Eigenschaft zu setzen.Und genau hier haben wir das Problem beobachtet. Ich habe gerade neu implementiert und die Hashcodes sind unterschiedlich. Ich muss nur ein paar Tage/Wochen warten, bis das Problem wieder auftritt, und den Klassen-Test erneut durchführen. –

+0

Sind diese beiden Klassen Unterklassen von irgendetwas? Sie haben möglicherweise einen Elternteil, der 'hashCode()' überschreibt. –

Antwort

4
  1. java.lang.Class enthebt nicht hashCode, noch JVM behandelt sie speziell irgendwie. Dies ist nur die reguläre Identität hashCode, die von java.lang.Object geerbt wurde.
  2. Wenn -XX:hashCode=0 (Standard in JDK 6 und JDK 7) der Identity-Hashcode mit globalen Park-Miller-Zufallszahlengenerator berechnet wird. Dieser Algorithmus erzeugt eindeutige Ganzzahlen mit der Periode 2^31-2, so dass es fast keine Chance gibt, dass zwei Objekte den gleichen Hash-Code haben, mit Ausnahme der folgenden Gründe.
  3. Da dieser Algorithmus auf einer globalen Variablen basiert, die nicht synchronisiert ist, gibt es tatsächlich eine Möglichkeit, dass zwei verschiedene Threads die gleiche Zufallszahl aufgrund der Racebedingung (the source) generieren. Dies ist anscheinend in Ihrem Fall passiert.
  4. Identität HashCode wird nicht bei der Erstellung von Objekten generiert, sondern beim ersten Aufruf der Methode hashCode. Es spielt also keine Rolle, wann und wie die Klassen geladen werden. Das Problem kann mit zwei beliebigen Objekten auftreten, wenn hashCode gleichzeitig aufgerufen wird.
  5. Ich empfehle die Verwendung -XX:hashCode=5 (Standard in JDK 8). Diese Option verwendet den Thread-lokalen XORShift-RNG. Es unterliegt keinen Rennbedingungen und ist auch schneller als der Park-Miller-Algorithmus.
+1

Wow, eine ausgezeichnete Erklärung! Ich war * sehr * skeptisch, dass der Classloader diese Klassen auf mehrere Threads initialisiert hat ... aber Ihre Erklärung macht das zu einem unwichtigen Punkt. Es ist sehr wahrscheinlich, dass diese Klassen ihren hashCode() zum ersten Mal durch gleichzeitige Threads aufgerufen haben, und ich stimme zu, dass dies die einzige wirkliche Erklärung ist. Aufgrund unserer Rahmenbeschränkungen ist Ihre Lösung zur Änderung der Hashcode-Generierungsstrategie meine beste Option. Bravo! –

Verwandte Themen