Je vais essayer de résumer trois versions moins raisonnables qui ont été proposées dans les commentaires.
@Holger dit :
Je suppose que c'est pour éviter que la classe java.util.Arrays ne soit chargée comme un effet secondaire de cette méthode. Pour le code de l'application, ce n'est généralement pas un problème.
C'est la chose la plus facile à tester. Compilons un tel programme :
public class HashMapTest {
public static void main(String[] args) {
new java.util.HashMap();
}
}
Exécutez-le avec java -verbose:class HashMapTest
. Cela imprimera les événements de chargement de classe au fur et à mesure qu'ils se produisent. Avec le JDK 1.8.0_60, je vois plus de 400 classes chargées :
... 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]
...
Comme vous pouvez le voir, HashMap
est chargé bien avant le code de l'application et Arrays
est chargé seulement 14 classes après HashMap
. Le site HashMap
La charge est déclenchée par sun.reflect.Reflection
initialisation comme il a HashMap
champs statiques. Le site Arrays
La charge est susceptible d'être déclenchée par WeakHashMap
charge qui a en fait Arrays.fill
dans le clear()
méthode. Le site WeakHashMap
La charge est déclenchée par java.lang.ClassValue$ClassValueMap
qui prolonge WeakHashMap
. Le site ClassValueMap
est présent dans chaque java.lang.Class
instance. Il me semble donc que sans Arrays
le JDK ne peut pas être initialisé du tout. De même, la classe Arrays
L'initialisateur statique est très court, il ne fait qu'initialiser le mécanisme d'assertion. Ce mécanisme est utilisé dans de nombreuses autres classes (dont, par exemple, java.lang.Throwable
qui est chargé très tôt). Aucune autre étape d'initialisation statique n'est effectuée dans java.util.Arrays
. La version de @Holger me semble donc incorrecte.
Ici, nous avons aussi trouvé des choses très intéressantes. Le site WeakHashMap.clear()
utilise toujours Arrays.fill
. C'est intéressant quand il est apparu là, mais malheureusement, cela va à temps préhistoriques (elle était déjà présente dans le tout premier dépôt public d'OpenJDK).
Ensuite, @MarcoTopolnik dit :
Plus sûr sûrement pas, mais il pourrait être plus rapide lorsque le fill
n'est pas inlined et tab
est court. Sur HotSpot, tant la boucle que l'explicite fill
aura pour résultat un compilateur intrinsèque rapide (dans un scénario de jour heureux).
C'était en fait surprenant pour moi que Arrays.fill
n'est pas directement intrinsiqué (voir liste intrinsèque généré par @apangin ). Il semble qu'une telle boucle puisse être reconnue et vectorisée par la JVM sans traitement intrinsèque explicite. Il est donc vrai que l'appel supplémentaire peut ne pas être inlined dans des cas très spécifiques (par exemple si MaxInlineLevel
est atteinte). D'un autre côté, il s'agit d'une situation très rare et d'un seul appel, ce n'est pas un appel à l'intérieur d'une boucle, et c'est un appel statique, pas virtuel/interface, donc l'amélioration des performances pourrait être seulement marginale et seulement dans certains scénarios spécifiques. Ce n'est pas ce qui intéresse généralement les développeurs de JVM.
Il convient également de noter que même le compilateur "client" de C1 (niveau 1-3) est capable de mettre en ligne les éléments suivants Arrays.fill
appelé, par exemple, dans WeakHashMap.clear()
comme le journal inlining ( -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
) dit :
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)
Bien entendu, le compilateur intelligent et puissant de C2 'server' permet également de l'intégrer facilement. Je ne vois donc aucun problème ici. Il semble que la version de @Marco ne soit pas correcte non plus.
Enfin, nous avons un couple de commentaires de @StuartMarks (qui est développeur de JDK, donc une voix officielle) :
Intéressant. Mon intuition est que c'est une erreur. Le fil de discussion pour cette modification est ici et il fait référence à un fil précédent c'est-à-dire suite ici . Le message initial de ce fil de discussion précédent indique un prototype de HashMap.java dans le dépôt CVS de Doug Lea. Je ne sais pas d'où cela vient. Il ne semble pas correspondre à quoi que ce soit dans l'historique d'OpenJDK.
... Quoi qu'il en soit, il pourrait s'agir d'un vieil instantané ; la boucle for se trouvait dans la méthode clear() depuis de nombreuses années. L'appel Arrays.fill() a été introduit par la méthode cette série de modifications Il n'a donc été présent dans l'arbre que pendant quelques mois. Notez également que le calcul de puissance de deux basé sur Integer.highestOneBit() introduit par cette série de modifications a également disparu au même moment, bien que cela ait été noté mais écarté pendant l'examen. Hmmm.
En effet, le HashMap.clear()
contenu la boucle de nombreuses années, a été a remplacé avec Arrays.fill
le 10 avril 2013 et est restée moins d'une demi-année jusqu'au 4 septembre, date à laquelle la discussion a commencé. commettre a été introduit. Le commit discuté était en fait une réécriture majeure de l'application HashMap
internes à réparer JDK-8023463 problème. C'était une longue histoire sur la possibilité d'empoisonner les HashMap
avec des clés ayant des codes de hachage en double, ce qui réduit HashMap
la vitesse de recherche est linéaire, ce qui la rend vulnérable aux attaques par déni de service. Les tentatives pour résoudre ce problème ont été effectuées dans le JDK-7, y compris une certaine randomisation du code de hachage des chaînes. Il semble donc que le HashMap
a été bifurquée du commit précédent, développée indépendamment, puis fusionnée dans la branche principale en écrasant plusieurs changements introduits entre-temps.
Nous pouvons soutenir cette hypothèse en effectuant un test de diff. Prenez le version où Arrays.fill
a été supprimé (2013-09-04) et le comparer avec version précédente (2013-07-30). Le site diff -U0
La sortie comporte 4341 lignes. Maintenant, faisons une comparaison avec le version avant un lorsque Arrays.fill
a été ajouté (2013-04-01). Maintenant diff -U0
ne contient que 2680 lignes. Ainsi, la nouvelle version ressemble davantage à l'ancienne qu'à son parent immédiat.
Conclusion
Pour conclure, je suis donc d'accord avec Stuart Marks. Il n'y avait aucune raison concrète d'enlever Arrays.fill
c'est juste parce que le changement entre les deux a été écrasé par erreur. En utilisant Arrays.fill
est tout à fait correct tant dans le code du JDK que dans les applications de l'utilisateur et utilisé, par exemple, dans WeakHashMap
. Le site Arrays
est chargée de toute façon assez tôt au cours de l'initialisation du JDK, elle possède un initialisateur statique très simple et une classe Arrays.fill
peut être facilement intégrée, même par le compilateur du client, de sorte qu'aucun inconvénient en termes de performances n'est à signaler.