就性能而言:
TL;DR
使用具有相似性能的 isInstance 或 instanceof。 isAssignableFrom 稍慢。
按表现排序:
- isInstance
-
instanceof (+ 0.5%)
-
isAssignableFrom (+ 2.7%)
基于 JAVA 8 Windows x64 上 2000 次迭代的基准,其中包含 20 次预热迭代。
理论上
使用像bytecode viewer 这样的软件,我们可以将每个运算符转换为字节码。
在以下情况下:
package foo;
public class Benchmark
{
public static final Object a = new A();
public static final Object b = new B();
...
}
JAVA:
b instanceof A;
字节码:
getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A
JAVA:
A.class.isInstance(b);
字节码:
ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Class isInstance((Ljava/lang/Object;)Z);
JAVA:
A.class.isAssignableFrom(b.getClass());
字节码:
ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Object getClass(()Ljava/lang/Class;);
invokevirtual java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z);
测量每个运算符使用了多少字节码指令,我们可以预期 instanceof 和 isInstance 比 isAssignableFrom 更快。但是,实际性能不是由字节码决定的,而是由机器码(取决于平台)决定的。让我们为每个算子做一个微基准测试。
基准
致谢:根据@aleksandr-dubinsky 的建议,感谢@yura 提供基本代码,这里有一个JMH 基准(参见tuning guide):
class A {}
class B extends A {}
public class Benchmark {
public static final Object a = new A();
public static final Object b = new B();
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testInstanceOf()
{
return b instanceof A;
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testIsInstance()
{
return A.class.isInstance(b);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testIsAssignableFrom()
{
return A.class.isAssignableFrom(b.getClass());
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(TestPerf2.class.getSimpleName())
.warmupIterations(20)
.measurementIterations(2000)
.forks(1)
.build();
new Runner(opt).run();
}
}
给出以下结果(分数是一个时间单位内的操作次数,所以分数越高越好):
Benchmark Mode Cnt Score Error Units
Benchmark.testIsInstance thrpt 2000 373,061 ± 0,115 ops/us
Benchmark.testInstanceOf thrpt 2000 371,047 ± 0,131 ops/us
Benchmark.testIsAssignableFrom thrpt 2000 363,648 ± 0,289 ops/us
警告
- 基准测试取决于 JVM 和平台。由于每个操作之间没有显着差异,因此在不同的 JAVA 版本和/或 Solaris、Mac 或 Linux 等平台上可能会得到不同的结果(也可能是不同的顺序!)。
- 基准比较了“B 是 A 的实例”在“B 直接扩展 A”时的性能。如果类层次结构更深更复杂(例如 B 扩展 X 扩展 Y 扩展 Z 扩展 A),结果可能会有所不同。
- 通常建议先编写代码,然后选择其中一个运算符(最方便),然后分析您的代码以检查是否存在性能瓶颈。也许这个运算符在您的代码上下文中可以忽略不计,或者也许......
- 相对于前一点,您的代码上下文中的
instanceof 可能比isInstance 更容易优化...
举个例子,采取以下循环:
class A{}
class B extends A{}
A b = new B();
boolean execute(){
return A.class.isAssignableFrom(b.getClass());
// return A.class.isInstance(b);
// return b instanceof A;
}
// Warmup the code
for (int i = 0; i < 100; ++i)
execute();
// Time it
int count = 100000;
final long start = System.nanoTime();
for(int i=0; i<count; i++){
execute();
}
final long elapsed = System.nanoTime() - start;
感谢 JIT,代码在某些时候得到了优化,我们得到:
- 实例:6ms
- isInstance:12ms
- isAssignableFrom : 15 毫秒
注意
最初这篇文章是在原始 JAVA 中使用 for 循环进行自己的基准测试,结果不可靠,因为某些优化(如 Just In Time)可以消除循环。所以它主要是测量 JIT 编译器优化循环需要多长时间:有关更多详细信息,请参阅 Performance test independent of the number of iterations
相关问题