Ich werde versuchen, drei mehr als vernünftige Versionen zusammenzufassen, die in Kommentaren vorgeschlagen wurden.
@ Holger sagt :
Ich denke, dies soll verhindern, dass die Klasse java.util.Arrays als Nebeneffekt dieser Methode geladen wird. Für Anwendungscode ist dies normalerweise kein Problem.
Dies ist am einfachsten zu testen. Lassen Sie uns ein solches Programm kompilieren:
public class HashMapTest {
public static void main(String[] args) {
new java.util.HashMap();
}
}
Führen Sie es mit java -verbose:class HashMapTest
. Dadurch werden die Klassenladeereignisse gedruckt, sobald sie auftreten. Mit JDK 1.8.0_60 werden mehr als 400 Klassen geladen:
... 155 lines skipped ...
[Loaded java.util.Set from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.AbstractSet from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$EmptySet from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$EmptyList from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$EmptyMap from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$UnmodifiableCollection from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$UnmodifiableList from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$UnmodifiableRandomAccessList from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.Reflection from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
**[Loaded java.util.HashMap from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.HashMap$Node from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.Class$3 from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.Class$ReflectionData from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.Class$Atomic from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.generics.repository.AbstractRepository from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.generics.repository.GenericDeclRepository from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.generics.repository.ClassRepository from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.Class$AnnotationData from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.annotation.AnnotationType from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.WeakHashMap from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.ClassValue$ClassValueMap from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.reflect.Modifier from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.LangReflectAccess from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.reflect.ReflectAccess from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
**[Loaded java.util.Arrays from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
...
Wie Sie sehen können, HashMap
wird lange vor dem Anwendungscode Arrays
geladen und nur 14 Klassen danach HashMap
. Das HashMap
Laden wird durch sun.reflect.Reflection
Initialisierung ausgelöst, da es HashMap
statische Felder enthält. Die Arrays
Last wird wahrscheinlich durch die WeakHashMap
Last ausgelöst , die tatsächlich Arrays.fill
in der clear()
Methode vorhanden ist. Die WeakHashMap
Last wird ausgelöst, durch java.lang.ClassValue$ClassValueMap
die sich erstreckt WeakHashMap
. Das ClassValueMap
ist in jedem java.lang.Class
Fall vorhanden. Mir scheint also, dass Arrays
das JDK ohne Klasse überhaupt nicht initialisiert werden kann. Auch der Arrays
statische Initialisierer ist sehr kurz, er initialisiert nur den Assertionsmechanismus. Dieser Mechanismus wird in vielen anderen Klassen verwendet (einschließlich beispielsweisejava.lang.Throwable
welches sehr früh geladen wird). In werden keine weiteren statischen Initialisierungsschritte ausgeführt java.util.Arrays
. Daher scheint mir die @ Holger-Version falsch zu sein.
Hier fanden wir auch sehr interessante Sache. Das WeakHashMap.clear()
benutzt noch Arrays.fill
. Es ist interessant, als es dort erschien, aber leider geht dies in prähistorische Zeiten (es war bereits im allerersten öffentlichen OpenJDK-Repository vorhanden).
Als nächstes sagt @MarcoTopolnik :
Sicherer nicht, aber es könnte schneller sein, wenn der fill
Anruf nicht inline und tab
kurz ist. Unter HotSpot führen sowohl die Schleife als auch der explizite fill
Aufruf zu einem schnellen Compiler (in einem Happy-Day-Szenario).
Es war tatsächlich überraschend für mich, dass Arrays.fill
es nicht direkt intrinsisch ist (siehe intrinsische Liste, die von @apangin generiert wurde ). Es scheint, dass eine solche Schleife von JVM ohne explizite intrinsische Behandlung erkannt und vektorisiert werden kann. Es ist also richtig, dass ein zusätzlicher Anruf in ganz bestimmten Fällen nicht eingebunden werden kann (z. B. wenn das MaxInlineLevel
Limit erreicht ist). Auf der anderen Seite ist es eine sehr seltene Situation und es ist nur ein einzelner Anruf, es ist kein Anruf innerhalb der Schleife und es ist ein statischer, kein virtueller / Schnittstellenaufruf, daher könnte die Leistungsverbesserung nur geringfügig und nur in bestimmten Szenarien sein. Nicht das, was die JVM-Entwickler normalerweise interessieren.
Es sollte auch beachtet werden, dass sogar der C1-Client-Compiler (Tier 1-3) inline Arrays.fill
aufgerufen werden kann, beispielsweise in WeakHashMap.clear()
, wie inlining log ( -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
) sagt:
36 3 java.util.WeakHashMap::clear (50 bytes)
!m @ 4 java.lang.ref.ReferenceQueue::poll (28 bytes)
@ 17 java.lang.ref.ReferenceQueue::reallyPoll (66 bytes) callee is too large
@ 28 java.util.Arrays::fill (21 bytes)
!m @ 40 java.lang.ref.ReferenceQueue::poll (28 bytes)
@ 17 java.lang.ref.ReferenceQueue::reallyPoll (66 bytes) callee is too large
@ 1 java.util.AbstractMap::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 9 java.lang.ref.ReferenceQueue::<init> (27 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 10 java.lang.ref.ReferenceQueue$Lock::<init> (5 bytes) unloaded signature classes
@ 62 java.lang.Float::isNaN (12 bytes) inline (hot)
@ 112 java.util.WeakHashMap::newTable (8 bytes) inline (hot)
Natürlich kann es auch leicht von einem intelligenten und leistungsstarken C2-Server-Compiler integriert werden. Somit sehe ich hier keine Probleme. Scheint, dass die @ Marco-Version auch falsch ist.
Zum Schluss haben wir noch ein paar Kommentare von @StuartMarks (der JDK-Entwickler ist, also eine offizielle Stimme):
Interessant. Meine Vermutung ist, dass dies ein Fehler ist. Der Überprüfungsthread für dieses Änderungsset befindet sich hier und verweist auf einen früheren Thread , der hier fortgesetzt wird . Die erste Nachricht in diesem früheren Thread verweist auf einen Prototyp von HashMap.java im CVS-Repository von Doug Lea. Ich weiß nicht, woher das kommt. Es scheint mit nichts in der OpenJDK-Geschichte übereinzustimmen.
... Auf jeden Fall könnte es ein alter Schnappschuss gewesen sein; Die for-Schleife war viele Jahre lang in der clear () -Methode. Der Aufruf von Arrays.fill () wurde von diesem Änderungssatz eingeführt , sodass er nur einige Monate im Baum enthalten war. Beachten Sie auch, dass die durch diesen Änderungssatz eingeführte Zweierpotenzberechnung basierend auf Integer.highestOneBit () zur gleichen Zeit ebenfalls verschwand, obwohl dies bemerkt, aber während der Überprüfung verworfen wurde. Hmmm.
Tatsächlich ist die HashMap.clear()
die Schleife vieler Jahre enthielt, wurde ersetzt mit Arrays.fill
am 10. April 2013 und blieb weniger ein ein halbes Jahr bis zum 4. September , wenn die diskutiert commit eingeführt wurde. Das besprochene Commit war tatsächlich eine wichtige Neufassung der HashMap
Interna, um das JDK-8023463- Problem zu beheben . Es war eine lange Geschichte über die Möglichkeit, die HashMap
mit Schlüsseln zu vergiften, die doppelte Hashcodes haben, wodurch die Suchgeschwindigkeit HashMap
auf linear reduziert wird , was sie für DoS-Angriffe anfällig macht. Die Versuche, dies zu lösen, wurden in JDK-7 durchgeführt, einschließlich einer Randomisierung des String-HashCodes. So scheint dasHashMap
Die Implementierung wurde aus dem früheren Commit herausgearbeitet, unabhängig entwickelt und dann in den Hauptzweig integriert, wobei mehrere dazwischen eingeführte Änderungen überschrieben wurden.
Wir können diese Hypothese unterstützen, indem wir einen Diff durchführen. Nehmen Sie die Version, in Arrays.fill
der sie entfernt wurde (04.09.2013), und vergleichen Sie sie mit der vorherigen Version (30.07.2013). Der diff -U0
Ausgang hat 4341 Zeilen. Lassen Sie uns nun gegen die Version vor einer Version unterscheiden , die Arrays.fill
hinzugefügt wurde (2013-04-01). diff -U0
Enthält jetzt nur noch 2680 Zeilen. Somit ähnelt die neuere Version tatsächlich eher der älteren als der unmittelbaren Eltern.
Fazit
Abschließend würde ich Stuart Marks zustimmen. Es gab keinen konkreten Grund zum Entfernen Arrays.fill
, nur weil die dazwischen liegende Änderung versehentlich überschrieben wurde. Die Verwendung Arrays.fill
ist sowohl im JDK-Code als auch in Benutzeranwendungen vollkommen in Ordnung und wird beispielsweise in verwendet WeakHashMap
. DasArrays
Klasse wird sowieso ziemlich früh während der JDK-Initialisierung geladen, verfügt über einen sehr einfachen statischen Initialisierer und die Arrays.fill
Methode kann auch vom Client-Compiler problemlos integriert werden, sodass kein Leistungsnachteil zu beachten ist.
fill
Anruf nicht inline undtab
kurz ist. Unter HotSpot führen sowohl die Schleife als auch der explizitefill
Aufruf zu einem schnellen Compiler (in einem Happy-Day-Szenario).