假设我们有三个(或更多)类
公共类 A {}
公共类 B 扩展 A {}
公共类 C 扩展 B 实现 G {}
假设每个类都有自己的 20 个(或更多)方法。
转换为 C 与转换为 A 对性能的影响更大吗?Java 强制转换是如何在幕后工作的?
向下投射时是否必须通过反射检查所有方法和字段是否存在?
编辑: 类的大小(字段和方法的数量)会影响投射时的性能吗?我对OpenJRE和Dalvik都感兴趣都感兴趣。
作为参考,我知道向上转换可以毫无问题地完成。
强制转换的性能取决于 JVM 实现。
JLS 5.5仅确定强制转换的要求(包括递归算法),但没有对实现设置任何要求。实际上5.5.3中的运行时强制转换规则也是以同样的方式确定的。所有产生与建议算法相同结果的 JVM 实现都被接受为适当的 JVM。
通常,C
由于 JVM 必须检查对象的运行时类型,因此强制转换为需要更多时间。当施放A
它时,没有理由做同样的检查,因为B
extends A
。
实际上,JVM 并不关心方法和字段的数量。它仅比较类型层次结构,您可以使用反射 ( o.getClass()
)进行检查
我做了一个示例代码如下,一个向下,然后一个向上:
Object o = new Integer(1);
Integer i = (Integer) o;
Object o2 = i;
编译后的字节码如下:
0 new java.lang.Integer [16]
3 dup
4 iconst_1 <-- 1 as a parameter to the constructor
5 invokespecial java.lang.Integer(int) [18] <-- constructor
8 astore_1 [o] <-- store in 'o'
9 aload_1 [o]
10 checkcast java.lang.Integer [16] <-- DOWNCAST CHECK, SPECIAL BYTECODE
13 astore_2 [i]
14 aload_2 [i]
15 astore_3 [o2] <-- WITH UPCAST NO CHECK
因此,有一条特定的 JVM 指令可以使用给定的类检查堆栈顶部的元素。
使用 upcast,根本没有检查。
类的大小(字段数、方法数、实际占用空间)无关紧要,因为转换会检查Class
(元数据,它实际上是一个对象)。
层次结构级别的数量以及实现接口的数量(如果转换为接口)确实很重要,因为这是要检查的可遍历继承/实现树。
如果此检查没有某种缓存,我会感到惊讶。
有关 HotSpot 中的详细架构checkcast
(在其他响应中提到作为向下转换的 JVM 机制),请查看此会议论文:
摘要中的引述:
在实际的基准测试运行中,我们的技术基本上一直在 3 条指令(仅 1 条内存引用)中执行完整的子类型检查。在极少数情况下,它会恢复为较慢的阵列扫描。内存使用量适中(每堂课 6 个单词),可以换取时间。
因此,如果您不编写一些非常低级的代码并进行大量转换,则影响可以忽略不计。