2015-09-02 14 views
10

Sto incontrando un problema di bizzarra su un server di JBoss in cui due classi producono lo stesso hashCode().due istanze di classe differenti che danno stesso 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())); 

produce:

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

io normalmente non mi importerebbe, ma stiamo usando un framework che memorizza nella cache setter utilizzando una chiave che si compone di codici hash della classe e un nome di proprietà. È un cattivo progetto per il caching, ma al momento è fuori controllo (OGNL 3.0.6 nell'ultimo Struts 2.3.24, vedi source. Un nuovo OGNL risolve il problema, ma non sarà in Struts fino al 2.5, attualmente in versione beta.)

ciò che rende la questione un po 'bizzarro per me è appare

  • problema dopo diversi giorni di utilizzo ... e sono abbastanza sicuro che sia di classe/proprietà sono sempre memorizzate nella cache in quel periodo. Questo mi porta a credere che l'hashcode dell'istanza di classe sia in realtà che cambia ... sono diventati uguali dopo diversi giorni.
  • Abbiamo osservato il comportamento in un Hotspot 1.6 obsoleto e ora in 1.7.0_80. Entrambi sono a 32 bit si basa su Sun Sparc
  • JVM riferisce XX: hashCode come "0"

ho letto che il generatore RNG codice hash a Hotspot ("0" strategia) in grado di produrre duplicati se non ci sono le corse thread, ma non riesco a immaginare il caricamento di classi che innesca quel comportamento.

fa Hotspot utilizzare un trattamento speciale codice hash quando si crea un'istanza Class?

+4

non credo che cambierà nel corso del tempo. Sarebbe molto strano Puoi verificare che non siano già gli stessi all'inizio? – Thilo

+0

@Thilo I non può essere al 100% che gli hashcode non siano sempre gli stessi. Tutto quello che so è che il meccanismo di memorizzazione nella cache che ho menzionato fallirebbe sicuramente in questa condizione quando si tenta di impostare una proprietà di azione del framework.E storicamente, è esattamente dove abbiamo osservato il problema. Sono stato riassegnato proprio ora e gli hashcode sono diversi. Dovrò solo attendere alcuni giorni/settimane fino al ritorno del problema ed eseguire di nuovo il test di classe. –

+0

Queste due classi sono sottoclassi di qualcosa? Potrebbero avere un genitore che esegue l'override di 'hashCode()'. –

risposta

4
  1. java.lang.Class non sovrascrive hashCode, nè JVM maniglie in qualche modo speciale. Questa è solo la normale identità di hashCode ereditata da java.lang.Object.
  2. Quando -XX:hashCode=0 (impostazione predefinita in JDK 6 e JDK 7) il hashCode identità è calcolato utilizzando globale Park-Miller generatore di numeri casuali. Questo algoritmo produce numeri interi univoci con il periodo 2^31-2, quindi non c'è quasi nessuna possibilità che due oggetti abbiano lo stesso hashCode tranne per il motivo sottostante.
  3. Da questo algoritmo si basa su una variabile globale che non è sincronizzato, v'è infatti possibile che due fili diversi generano lo stesso numero casuale dovuto condizione di competizione (the source). Questo è ciò che apparentemente accade nel tuo caso.
  4. identità hashCode non viene generato sulla creazione dell'oggetto, ma la prima chiamata al metodo hashCode. Quindi non importa quando e come vengono caricate le classi. Il problema può verificarsi con due oggetti qualsiasi se hashCode viene chiamato contemporaneamente.
  5. Suggerisco di utilizzare -XX:hashCode=5 (impostazione predefinita in JDK 8). Questa opzione utilizza XShift RNG locale thread. Non è soggetto a condizioni di gara ed è anche più veloce dell'algoritmo di Park-Miller.
+1

Wow, un'ottima spiegazione! Ero * molto * scettico sul classloader inizializzando queste classi in un modo multithreading ... ma la tua spiegazione lo rende un punto irrilevante. È * molto * possibile che queste classi avessero il loro hashCode() chiamato per la prima volta da thread simultanei, e sono d'accordo che questa è l'unica vera spiegazione. A causa dei nostri vincoli di struttura, la soluzione migliore per cambiare la strategia di generazione del codice hash è la soluzione migliore. Bravo! –

Problemi correlati