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, HashMapwird lange vor dem Anwendungscode Arraysgeladen und nur 14 Klassen danach HashMap. Das HashMapLaden wird durch sun.reflect.ReflectionInitialisierung ausgelöst, da es HashMapstatische Felder enthält. Die ArraysLast wird wahrscheinlich durch die WeakHashMapLast ausgelöst , die tatsächlich Arrays.fillin der clear()Methode vorhanden ist. Die WeakHashMapLast wird ausgelöst, durch java.lang.ClassValue$ClassValueMapdie sich erstreckt WeakHashMap. Das ClassValueMapist in jedem java.lang.ClassFall vorhanden. Mir scheint also, dass Arraysdas JDK ohne Klasse überhaupt nicht initialisiert werden kann. Auch der Arraysstatische Initialisierer ist sehr kurz, er initialisiert nur den Assertionsmechanismus. Dieser Mechanismus wird in vielen anderen Klassen verwendet (einschließlich beispielsweisejava.lang.Throwablewelches 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 fillAnruf nicht inline und tabkurz ist. Unter HotSpot führen sowohl die Schleife als auch der explizite fillAufruf zu einem schnellen Compiler (in einem Happy-Day-Szenario).
Es war tatsächlich überraschend für mich, dass Arrays.filles 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 MaxInlineLevelLimit 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.fillaufgerufen 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.fillam 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 HashMapInterna, um das JDK-8023463- Problem zu beheben . Es war eine lange Geschichte über die Möglichkeit, die HashMapmit Schlüsseln zu vergiften, die doppelte Hashcodes haben, wodurch die Suchgeschwindigkeit HashMapauf 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.fillder sie entfernt wurde (04.09.2013), und vergleichen Sie sie mit der vorherigen Version (30.07.2013). Der diff -U0Ausgang hat 4341 Zeilen. Lassen Sie uns nun gegen die Version vor einer Version unterscheiden , die Arrays.fillhinzugefügt wurde (2013-04-01). diff -U0Enthä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.fillist 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.fillMethode kann auch vom Client-Compiler problemlos integriert werden, sodass kein Leistungsnachteil zu beachten ist.
fillAnruf nicht inline undtabkurz ist. Unter HotSpot führen sowohl die Schleife als auch der explizitefillAufruf zu einem schnellen Compiler (in einem Happy-Day-Szenario).