arrays shot JAVA_java – 为什么Arrays.fill()在HashMap.clear()中不再使用?

我将尝试总结在评论中提出的三个更无意义的合理版本。

@Holger says:

I guess that this is to avoid the class java.util.Arrays getting loading as a side effect of this method. For application code, this is usually not a concern.

这是最容易的事情要测试。让我们编译一下这样的程序:

public class HashMapTest {

public static void main(String[] args) {

new java.util.HashMap();

}

}

使用java -verbose运行它:class HashMapTest。这将在类加载事件发生时打印它们。使用JDK 1.8.0_60我看到400多个类加载:

... 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]

...

如你所见,HashMap在应用程序代码之前加载,而Arrays只在HashMap之后加载14个类。 HashMap加载由sun.reflect.Reflection初始化触发,因为它具有HashMap静态字段。数组负载可能由WeakHashMap负载触发,实际上在clear()方法中具有Arrays.fill。 WeakHashMap加载由扩展WeakHashMap的java.lang.ClassValue $ ClassValueMap触发。 ClassValueMap存在于每个java.lang.Class实例中。所以对我来说似乎没有Arrays类的JDK不能被初始化。此外,Arrays static初始化器非常短,它只初始化断言机制。此机制用于许多其他类(包括,例如,很早加载的java.lang.Throwable)。在java.util.Arrays中不执行任何其他静态初始化步骤。因此@Holger版本似乎不正确。

在这里我们还发现了很有趣的事情。 WeakHashMap.clear()仍然使用Arrays.fill。它很有趣,当它出现在那里,但不幸的是,这到prehistoric times(它已经在第一个公开的OpenJDK存储库)。

接下来,@MarcoTopolnik says:

Safer surely not, but it might be faster when the fill call is not inlined and tab is short. On HotSpot both the loop and the explicit fill call will result in a fast compiler intrinsic (in a happy-day scenario).

实际上,令我惊讶的是,Arrays.fill没有直接内在化(参见intrinsic list由@apangin生成)。似乎这样的循环可以被识别和矢量化的JVM没有显式的内在处理。所以这是真的,额外的调用可以不内联在非常具体的情况下(例如,如果达到MaxInlineLevel限制)。另一方面,这是非常罕见的情况,它只是一个单独的调用,它不是一个循环内的调用,它是一个静态的,而不是虚拟/接口调用,因此性能的提高只能是边缘的,只有在一些特定的场景。不是JVM开发人员通常关心的事情。

还应该注意,即使C1’客户端’编译器(层1-3)能够将例如在WeakHashMap.clear()中调用的Arrays.fill内联为内联日志(-XX:UnlockDiagnosticVMOptions -XX:PrintCompilation – XX:PrintInlining)说:

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:: (5 bytes) inline (hot)

@ 1 java.lang.Object:: (1 bytes) inline (hot)

@ 9 java.lang.ref.ReferenceQueue:: (27 bytes) inline (hot)

@ 1 java.lang.Object:: (1 bytes) inline (hot)

@ 10 java.lang.ref.ReferenceQueue$Lock:: (5 bytes) unloaded signature classes

@ 62 java.lang.Float::isNaN (12 bytes) inline (hot)

@ 112 java.util.WeakHashMap::newTable (8 bytes) inline (hot)

当然,它也很容易内联的智能和强大的C2’服务器’编译器。因此,我看到没有问题。似乎@Marco版本不正确。

最后,我们有一对来自@StuartMarks的comments(谁是JDK开发人员,因此有一些官方的声音):

Interesting. My hunch is that this is a mistake. The review thread for this changeset is 07006 and it references an 07007 that is 07008. The initial message in that earlier thread points to a prototype of HashMap.java in Doug Lea’s CVS repository. I don’t know where this came from. It doesn’t seem to match anything in the OpenJDK history.

… In any case, it might have been some old snapshot; the for-loop was in the clear() method for many years. The Arrays.fill() call was introduced by 07009, so it was in the tree only for a few months. Note also that the power-of-two computation based on Integer.highestOneBit() introduced by 070010 also disappeared at the same time, though this was noted but dismissed during the review. Hmmm.

事实上,HashMap.clear()包含循环多年,2013年4月10日与Arrays.fill的replaced,并保持少一个半年,直到9月4日,当讨论的commit被介绍。讨论的提交实际上是一个重大的HashMap内部修复JDK-8023463问题。这是一个长的故事,关于可能性的毒性HashMap与键具有重复的哈希编码减少HashMap搜索速度到线性,使其易受DoS攻击。解决这个问题的尝试是在JDK-7中执行的,包括一些String hashCode的随机化。所以似乎HashMap实现是从早期的提交,独立开发,然后合并到主分支覆盖在中间介绍的几个更改。

我们可以支持这种假设执行差异。取走version,其中Arrays.fill被删除(2013-09-04),并与previous version(2013-07-30)进行比较。 diff -U0输出有4341行。现在让我们对比添加Arrays.fill之前的version(2013-04-01)。现在diff -U0只包含2680行。因此,较新的版本实际上更类似于比直接父类更老。

结论

所以最后我同意Stuart Marks。没有具体的理由删除Arrays.fill,它只是因为中间的更改被错误覆盖。使用Arrays.fill在JDK代码和用户应用程序中都是非常好的,例如在WeakHashMap中使用。 Arrays类在JDK初始化期间很早就加载,有非常简单的静态初始化器和Arrays.fill方法甚至可以很容易地由客户端编译器内联,所以没有性能缺点应该注意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值