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

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

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

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


当前回答

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.

其他回答

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

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

我基于jmh-java-benchmark- prototype:2.21编写了一个性能测试。JDK为openjdk, version为1.8.0_212。测试机器是mac pro。 测试结果为:

Benchmark                Mode  Cnt    Score   Error   Units
MyBenchmark.getClasses  thrpt   30  510.818 ± 4.190  ops/us
MyBenchmark.instanceOf  thrpt   30  503.826 ± 5.546  ops/us

结果表明:getClass优于instanceOf,这与其他测试结果相反。然而,我不知道为什么。

测试代码如下:

public class MyBenchmark {

public static final Object a = new LinkedHashMap<String, String>();

@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean instanceOf() {
    return a instanceof Map;
}

@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean getClasses() {
    return a.getClass() == HashMap.class;
}

public static void main(String[] args) throws RunnerException {
    Options opt =
        new OptionsBuilder().include(MyBenchmark.class.getSimpleName()).warmupIterations(20).measurementIterations(30).forks(1).build();
    new Runner(opt).run();
}
}

You're focusing on the wrong thing. The difference between instanceof and any other method for checking the same thing would probably not even be measurable. If performance is critical then Java is probably the wrong language. The major reason being that you can't control when the VM decides it wants to go collect garbage, which can take the CPU to 100% for several seconds in a large program (MagicDraw 10 was great for that). Unless you are in control of every computer this program will run on you can't guarantee which version of JVM it will be on, and many of the older ones had major speed issues. If it's a small app you may be ok with Java, but if you are constantly reading and discarding data then you will notice when the GC kicks in.

现代JVM/JIT编译器已经消除了大多数传统的“慢”操作对性能的影响,包括instanceof、异常处理、反射等。

正如Donald Knuth所写的,“我们应该忘记小的效率,大约97%的时候:过早的优化是万恶之源。”instanceof的性能可能不是问题,所以在确定问题所在之前,不要浪费时间想出奇异的解决方案。

如果速度是您的唯一目标,那么使用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%的总速度提高。