Ich habe mir den Bytecode angeschaut, wie @user kommentierte, es ist wahrscheinlich eine Optimierung, um den getfield
Aufruf innerhalb der Schleife zu vermeiden. Aber sie haben versagt und verweisen immer noch auf die Wertvariable in der Schleifenbedingung ... so macht dies den Bytecode tatsächlich länger und langsamer.
public int h1() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
public int h2() {
int h = hash;
if (h == 0 && value.length > 0) {
for (int i = 0; i < value.length; i++) {
h = 31 * h + value[i];
}
hash = h;
}
return h;
}
Wir können sehen, dass beide Methoden fast identisch Bytecode erzeugen, aber unsere „optimiert“ Implementierung endet eigentlich 2 weitere Anrufe auf verwenden.
Beachten Sie, wie der for-Loop-Test (Zeilen 39-45 in h1 und Zeilen 37-43 in h2) getfield aufruft, um den Aufruf der Array-Länge auszuführen.
Bytecode:
public int h1();
Code:
0: aload_0
1: getfield #17 // Field hash:I
4: istore_1
5: iload_1
6: ifne 53
9: aload_0
10: getfield #15 // Field value:[C
13: arraylength
14: ifle 53
17: aload_0
18: getfield #15 // Field value:[C
21: astore_2
22: iconst_0
23: istore_3
24: goto 39
27: bipush 31
29: iload_1
30: imul
31: aload_2
32: iload_3
33: caload
34: iadd
35: istore_1
36: iinc 3, 1
39: iload_3
40: aload_0
41: getfield #15 // Field value:[C
44: arraylength
45: if_icmplt 27
48: aload_0
49: iload_1
50: putfield #17 // Field hash:I
53: iload_1
54: ireturn
public int h2();
Code:
0: aload_0
1: getfield #17 // Field hash:I
4: istore_1
5: iload_1
6: ifne 51
9: aload_0
10: getfield #15 // Field value:[C
13: arraylength
14: ifle 51
17: iconst_0
18: istore_2
19: goto 37
22: bipush 31
24: iload_1
25: imul
26: aload_0
27: getfield #15 // Field value:[C
30: iload_2
31: caload
32: iadd
33: istore_1
34: iinc 2, 1
37: iload_2
38: aload_0
39: getfield #15 // Field value:[C
42: arraylength
43: if_icmplt 22
46: aload_0
47: iload_1
48: putfield #17 // Field hash:I
51: iload_1
52: ireturn
Wenn sie die Schleifenbedingung auch geändert hatte, um das lokale Feld zu verwenden,
...
for (int i = 0; i < val.length; i++) {
...
Dann wird der Bytecode tatsächlich wird kürzer und verliert den wohl langsamer getfield Anruf in die Schleife,
public int h1();
Code:
0: aload_0
1: getfield #17 // Field hash:I
4: istore_1
5: iload_1
6: ifne 50
9: aload_0
10: getfield #15 // Field value:[C
13: arraylength
14: ifle 50
17: aload_0
18: getfield #15 // Field value:[C
21: astore_2
22: iconst_0
23: istore_3
24: goto 39
27: bipush 31
29: iload_1
30: imul
31: aload_2
32: iload_3
33: caload
34: iadd
35: istore_1
36: iinc 3, 1
39: iload_3
40: aload_2
41: arraylength
42: if_icmplt 27
45: aload_0
46: iload_1
47: putfield #17 // Field hash:I
50: iload_1
51: ireturn
Ein dummer Test, nur einige Male über die Methode zu schlendern, auf meiner jdk 1.7.0_79 zeigt immer wieder, dass die ursprüngliche Methode urkomisch ungefähr 5 mal länger dauert, um dann entweder die un "optimierte" oder die richtig "optimierte" zu laufen Methoden. Die letzteren 2 haben keinen Unterschied in der Leistung.
Also abschließend denke ich, Speichern des Feldes lokal war ein Versuch, die Methoden Bytecode zu optimieren, wahrscheinlich bevor der Jit konnte dies selbst zu optimieren, aber sie vermasselt es und tatsächlich machte die Methode viel schlimmer ...
Dieser Code ist in Java 9 pro tatsächlich fixiert, https://bugs.openjdk.java.net/browse/JDK-8058643
Sicher, ich denke, es ist überflüssig und kann nicht verstehen, warum. – hata
Es gibt keinen Vorteil. Selbst sie machen keine Kopie davon. Es hat nur zusätzliche Referenz. Schön, das hast du herausgefunden 1. – rajuGT
Wer öffnet einen Fehlerbericht? – lschuetze