我正在开发一个应用程序,其中一个设计方法涉及到大量使用instanceof操作符。虽然我知道OO设计通常试图避免使用instanceof,但那是另一回事,这个问题纯粹与性能有关。我想知道是否对性能有影响?和==一样快吗?

例如,我有一个有10个子类的基类。在接受基类的单个函数中,我检查类是否是子类的实例并执行一些例程。

我想到的另一种解决方法是使用“type id”整数原语,并使用位掩码来表示子类的类别,然后对子类“type id”与表示类别的常量掩码进行位掩码比较。

instanceof是否被JVM优化得更快?我想坚持使用Java,但应用程序的性能至关重要。如果有人曾经在这条路上走过,可以提供一些建议,那就太棒了。我是不是太挑剔了,或者专注在错误的地方去优化?


当前回答

很难说一个特定的JVM是如何实现实例的,但在大多数情况下,对象与结构相当,类也是如此,每个对象结构都有一个指向它是实例的类结构的指针。实际上是instanceof for

if (o instanceof java.lang.String)

可能和下面的C代码一样快

if (objectStruct->iAmInstanceOf == &java_lang_String_class)

假设JIT编译器已经就位,并且工作出色。

考虑到这只是访问一个指针,在指针指向的某个偏移量处获得一个指针,并将其与另一个指针进行比较(这基本上与测试32位数字是否相等相同),我认为操作实际上可以非常快。

但是,这并不一定是必须的,它在很大程度上取决于JVM。但是,如果这将成为代码中的瓶颈操作,我认为JVM实现相当糟糕。即使没有JIT编译器,只解释代码,也应该能够在几乎没有时间的情况下创建一个实例测试。

其他回答

Instanceof非常快。它可以归结为用于类引用比较的字节码。在一个循环中尝试几百万个实例,自己看看。

如果它确实是项目中的性能问题,您应该进行测量/分析。如果是的话,我建议重新设计——如果可能的话。我很确定你不能打败平台的本机实现(用C编写)。在这种情况下,你还应该考虑多重继承。

你应该告诉更多关于这个问题,也许你可以使用一个关联存储,例如Map<Class,对象>如果你只对具体类型感兴趣。

方法

我写了一个基准程序来评估不同的实现:

实例实现(作为参考) 通过抽象类和@Override测试方法实现面向对象 使用自己的类型实现 getClass() == _.class实现

我使用jmh运行了100个预热调用、1000个测试迭代和10个分支的基准测试。因此,每个选项都测量了10,000次,在我的MacBook Pro上运行macOS 10.12.4和Java 1.8的整个基准测试需要12:18:57。该基准衡量每个选项的平均时间。要了解更多细节,请参阅我在GitHub上的实现。

为了完整起见:这个答案和我的基准有一个以前的版本。

结果

| Operation  | Runtime in nanoseconds per operation | Relative to instanceof |
|------------|--------------------------------------|------------------------|
| INSTANCEOF | 39,598 ± 0,022 ns/op                 | 100,00 %               |
| GETCLASS   | 39,687 ± 0,021 ns/op                 | 100,22 %               |
| TYPE       | 46,295 ± 0,026 ns/op                 | 116,91 %               |
| OO         | 48,078 ± 0,026 ns/op                 | 121,42 %               |

博士tl;

在Java 1.8中,instanceof是最快的方法,尽管getClass()非常接近。

如果速度是您的唯一目标,那么使用int常量来标识子类似乎可以节省几毫秒的时间

static final int ID_A = 0;
static final int ID_B = 1;
abstract class Base {
  final int id;
  Base(int i) { id = i; }
}
class A extends Base {
 A() { super(ID_A); }
}
class B extends Base {
 B() { super(ID_B); }
}
...
Base obj = ...
switch(obj.id) {
case  ID_A: .... break;
case  ID_B: .... break;
}

糟糕的OO设计,但如果你的性能分析表明这是你的瓶颈,那么也许。在我的代码中,分派代码占用了总执行时间的10%,这可能有助于1%的总速度提高。

I'll get back to you on instanceof performance. But a way to avoid problem (or lack thereof) altogether would be to create a parent interface to all the subclasses on which you need to do instanceof. The interface will be a super set of all the methods in sub-classes for which you need to do instanceof check. Where a method does not apply to a specific sub-class, simply provide a dummy implementation of this method. If I didn't misunderstand the issue, this is how I've gotten around the problem in the past.