Zehn Jahre später haben sich die Dinge geändert. Ich kann das ehrlich gesagt nicht glauben (aber der Geek in mir ist extrem glücklich).
Wie Sie bemerkt haben, gibt es Chancen, wo einige String::hashCode
für einige Saiten sind zero
und dies war nicht im Cache gespeichert (wird dazu kommen). Viele Leute argumentierten (auch in diesem Q & A), warum es kein zusätzliches Feld gab java.lang.String
, so etwas wie: hashAlreadyComputed
und verwenden Sie das einfach. Das Problem liegt auf der Hand: zusätzlicher Speicherplatz für jede einzelne String-Instanz. Es gibt übrigens einen Grund, der java-9
eingeführt compact String
wurde, weil viele Benchmarks gezeigt haben, dass dies in den meisten Anwendungen eine eher ( über- ) verwendete Klasse ist. Mehr Platz hinzufügen ? Die Entscheidung war: nein. Vor allem , da die kleinste mögliche Ergänzung gewesen wäre 1 byte
, nicht 1 bit
(für 32 bit JMV
s, würde der zusätzliche Platz gewesen8 bytes
: 1 für die Flagge, 7 für die Ausrichtung).
Also, Compact String
s kam herein java-9
, und wenn Sie genau hinschauen (oder sich darum kümmern), werden sie taten in ein Feld hinzufügen java.lang.String
: coder
. Habe ich nicht einfach dagegen gestritten? Es ist nicht so leicht. Es scheint, dass die Bedeutung kompakter Zeichenfolgen das Argument "zusätzlicher Platz" überwogen hat. Es ist auch wichtig zu sagen, dass zusätzlicher Platz 32 bits VM
nur für wichtig ist (weil es keine Lücke in der Ausrichtung gab). Im Gegensatz dazu ist im jdk-8
Layout von java.lang.String
:
java.lang.String object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 char[] String.value N/A
16 4 int String.hash N/A
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Beachten Sie genau dort eine wichtige Sache:
Space losses : ... 4 bytes total.
Da jedes Java-Objekt ausgerichtet ist (wie stark es von der JVM und einigen Startflags abhängt, wie UseCompressedOops
zum Beispiel), String
gibt es eine Lücke 4 bytes
, die nicht verwendet wird. Beim Hinzufügen coder
dauerte es einfach 1 byte
ohne zusätzlichen Speicherplatz hinzuzufügen. Als solche nach Compact String
s hinzugefügt wurden, hat das Layout geändert:
java.lang.String object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 byte[] String.value N/A
16 4 int String.hash N/A
20 1 byte String.coder N/A
21 3 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
coder
isst 1 byte
und die Lücke wurde geschrumpft3 bytes
. Der "Schaden" wurde also schon in angerichtet jdk-9
. Denn 32 bits JVM
es gab eine Zunahme mit 8 bytes : 1 coder + 7 gap
und für 64 bit JVM
- es gab keine Zunahme, coder
die etwas Platz von der Lücke einnahm.
Und jetzt in jdk-13
sie beschlossen, das zu nutzen gap
, da es sowieso existiert. Ich möchte Sie nur daran erinnern, dass die Wahrscheinlichkeit, einen String mit null Hashcode zu haben, 1 zu 4 Milliarden beträgt. Es gibt immer noch Leute, die sagen: na und? Lass uns das beheben! Voilá: jdk-13
Layout von java.lang.String
:
java.lang.String object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 byte[] String.value N/A
16 4 int String.hash N/A
20 1 byte String.coder N/A
21 1 boolean String.hashIsZero N/A
22 2 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 2 bytes external = 2 bytes total
Und hier ist es : boolean String.hashIsZero
. Und hier ist es in der Codebasis:
public int hashCode() {
int h = hash;
if (h == 0 && !hashIsZero) {
h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
if (h == 0) {
hashIsZero = true;
} else {
hash = h;
}
}
return h;
}
Warten! h == 0
und hashIsZero
Feld? Sollte das nicht so heißen wie : hashAlreadyComputed
? Warum ist die Implementierung nicht so:
@Override
public int hashCode(){
if(!hashCodeComputed){
hash = 42;
hashCodeComputed = true;
}
return hash;
}
Auch wenn ich den Kommentar unter dem Quellcode gelesen habe:
Es ist nur sinnvoll , nachdem ich gelesen dies . Ziemlich knifflig, aber dies schreibt man nach dem anderen, viel mehr Details in der obigen Diskussion.