在J中使用instanceof的性能影响

我正在开发一个应用程序,一种设计方法涉及到instanceof运算符的极大使用。 虽然我知道OO设计通常会试图避免使用instanceof,但这是一个不同的故事,这个问题纯粹与性能有关。 我想知道是否有任何性能影响? 是和==一样快吗?

例如,我有一个包含10个子类的基类。 在一个获取基类的函数中,我会检查该类是否是子类的实例并执行一些例程。

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

instanceof以某种方式由JVM优化得比这更快? 我想坚持使用Java,但应用程序的性能至关重要。 如果之前一直走在这条路上的人可以提供一些建议,那将会很酷。 我是在挑剔太多还是专注于错误的事情来优化?

23个解决方案

244 votes

现代JVM / JIC编译器已经消除了大多数传统上“慢”操作的性能损失,包括实例,异常处理,反射等。

正如唐纳德·克努特(Donald Knuth)所写的那样,“我们应该忘记小的效率,大约97%的时间说:过早的优化是所有邪恶的根源。” instanceof的性能可能不会成为一个问题,所以不要浪费你的时间来提出异乎寻常的解决方法,直到你确定这是问题为止。

Steve answered 2019-02-08T14:42:46Z

223 votes

途径

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

instanceof实施(作为参考)

面向对象的抽象类和instanceof测试方法

使用自己的类型实现

instanceof实施

我使用jmh运行基准测试,有100个热身调用,1000个迭代测量,10个分叉。 所以每个选项都测量了10000次,需要12:18:57才能在我的MacBook Pro上使用macOS 10.12.4和Java 1.8运行整个基准测试。 基准测量每个选项的平均时间。 有关更多详细信息,请参阅我在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()非常接近。

Michael Dorner answered 2019-02-08T14:44:13Z

72 votes

我只是做了一个简单的测试,看看instanceOf性能是如何与只有一个字母的字符串对象的简单s.equals()调用进行比较的。

在10.000.000循环中,instanceOf给了我63-96ms,字符串等于给了我106-230ms

我用java jvm 6。

所以在我的简单测试中,更快做一个instanceOf而不是一个字符串比较。

使用Integer的.equals()代替字符串给了我相同的结果,只有当我使用== i比instanceOf快20ms(在10.000.000循环中)时

answered 2019-02-08T14:45:13Z

17 votes

决定性能影响的项目是:

instanceof运算符可能返回true的可能类的数量

您的数据分布 - 是在第一次或第二次尝试中解决的大多数操作实例? 你最想让你最有可能返回真正的操作。

部署环境。 在Sun Solaris VM上运行与Sun的Windows JVM有很大不同。 默认情况下,Solaris将以“服务器”模式运行,而Windows将以客户端模式运行。 Solaris上的JIT优化将使所有方法访问都相同。

我为四种不同的调度方法创建了一个微基准测试。 Solaris的结果如下,较小的数字更快:

InstanceOf 3156

class== 2925

OO 3083

Id 3067

brianegge answered 2019-02-08T14:46:13Z

16 votes

回答你的最后一个问题:除非探查者告诉你,你在一个实例中花费了大量的时间:是的,你是在挑剔。

在想要优化从未需要优化的东西之前:以最易读的方式编写算法并运行它。 运行它,直到jit编译器有机会自己优化它。 如果您在使用这段代码时遇到问题,请使用分析器告诉您,从哪里获得最大收益并对其进行优化。

在高度优化编译器的时候,你对瓶颈的猜测可能是完全错误的。

并且本着这个答案的真正精神(我完全相信):一旦jit-compiler有机会优化它,我绝对不知道instanceof和==如何关联。

我忘记了:永远不要测量第一次跑步。

Olaf Kock answered 2019-02-08T14:47:24Z

12 votes

我有同样的问题,但因为我没有找到与我的用例类似的“性能指标”,我已经做了一些示例代码。 在我的硬件和Java 6& 7,instanceof和10mln迭代之间的区别是

for 10 child classes - instanceof: 1200ms vs switch: 470ms

for 5 child classes - instanceof: 375ms vs switch: 204ms

因此,instanceof确实比较慢,尤其是在大量的if-else-if语句中,但是在实际应用中差异可以忽略不计。

import java.util.Date;

public class InstanceOfVsEnum {

public static int c1, c2, c3, c4, c5, c6, c7, c8, c9, cA;

public static class Handler {

public enum Type { Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Type9, TypeA }

protected Handler(Type type) { this.type = type; }

public final Type type;

public static void addHandlerInstanceOf(Handler h) {

if( h instanceof H1) { c1++; }

else if( h instanceof H2) { c2++; }

else if( h instanceof H3) { c3++; }

else if( h instanceof H4) { c4++; }

else if( h instanceof H5) { c5++; }

else if( h instanceof H6) { c6++; }

else if( h instanceof H7) { c7++; }

else if( h instanceof H8) { c8++; }

else if( h instanceof H9) { c9++; }

else if( h instanceof HA) { cA++; }

}

public static void addHandlerSwitch(Handler h) {

switch( h.type ) {

case Type1: c1++; break;

case Type2: c2++; break;

case Type3: c3++; break;

case Type4: c4++; break;

case Type5: c5++; break;

case Type6: c6++; break;

case Type7: c7++; break;

case Type8: c8++; break;

case Type9: c9++; break;

case TypeA: cA++; break;

}

}

}

public static class H1 extends Handler { public H1() { super(Type.Type1); } }

public static class H2 extends Handler { public H2() { super(Type.Type2); } }

public static class H3 extends Handler { public H3() { super(Type.Type3); } }

public static class H4 extends Handler { public H4() { super(Type.Type4); } }

public static class H5 extends Handler { public H5() { super(Type.Type5); } }

public static class H6 extends Handler { public H6() { super(Type.Type6); } }

public static class H7 extends Handler { public H7() { super(Type.Type7); } }

public static class H8 extends Handler { public H8() { super(Type.Type8); } }

public static class H9 extends Handler { public H9() { super(Type.Type9); } }

public static class HA extends Handler { public HA() { super(Type.TypeA); } }

final static int cCycles = 10000000;

public static void main(String[] args) {

H1 h1 = new H1();

H2 h2 = new H2();

H3 h3 = new H3();

H4 h4 = new H4();

H5 h5 = new H5();

H6 h6 = new H6();

H7 h7 = new H7();

H8 h8 = new H8();

H9 h9 = new H9();

HA hA = new HA();

Date dtStart = new Date();

for( int i = 0; i < cCycles; i++ ) {

Handler.addHandlerInstanceOf(h1);

Handler.addHandlerInstanceOf(h2);

Handler.addHandlerInstanceOf(h3);

Handler.addHandlerInstanceOf(h4);

Handler.addHandlerInstanceOf(h5);

Handler.addHandlerInstanceOf(h6);

Handler.addHandlerInstanceOf(h7);

Handler.addHandlerInstanceOf(h8);

Handler.addHandlerInstanceOf(h9);

Handler.addHandlerInstanceOf(hA);

}

System.out.println("Instance of - " + (new Date().getTime() - dtStart.getTime()));

dtStart = new Date();

for( int i = 0; i < cCycles; i++ ) {

Handler.addHandlerSwitch(h1);

Handler.addHandlerSwitch(h2);

Handler.addHandlerSwitch(h3);

Handler.addHandlerSwitch(h4);

Handler.addHandlerSwitch(h5);

Handler.addHandlerSwitch(h6);

Handler.addHandlerSwitch(h7);

Handler.addHandlerSwitch(h8);

Handler.addHandlerSwitch(h9);

Handler.addHandlerSwitch(hA);

}

System.out.println("Switch of - " + (new Date().getTime() - dtStart.getTime()));

}

}

Xtra Coder answered 2019-02-08T14:48:03Z

8 votes

X非常快,仅需几条CPU指令。

显然,如果类X没有加载子类(JVM知道),instanceof可以优化为:

x instanceof X

==> x.getClass()==X.class

==> x.classID == constant_X_ID

主要成本只是阅读!

如果X确实加载了子类,则需要更多的读取; 它们可能位于同一地点,因此额外成本也非常低。

大家好消息!

irreputable answered 2019-02-08T14:49:07Z

5 votes

在大多数现实世界的实现中,instanceof可能比简单的equals更昂贵(也就是真正需要instanceof的那些,你不能通过覆盖常用方法来解决它,比如每个初学者教科书以及 上面的Demian建议)。

这是为什么? 因为可能会发生的事情是你有几个接口,提供一些功能(比方说,接口x,y和z),以及一些操纵的对象,可能(或不)实现其中一个接口...但是 不直接。 比方说,我有:

w延伸x

工具w

B延伸A.

C扩展B,实现y

D扩展C,实现z

假设我正在处理对象d的D实例。 计算(d instanceof x)需要采用d.getClass(),循环通过它实现的接口来知道一个是否是==到x,如果不是为了所有的祖先再次递归...在我们的例子中,如果你对该树进行广泛的第一次探索,产生至少8次比较,假设y和z不扩展任何东西......

现实世界派生树的复杂性可能更高。 在某些情况下,JIT可以优化其中的大部分,如果它能够在所有可能的情况下提前解析为扩展x的某个实例。 然而,实际上,您将在大多数时间通过树遍历。

如果这成为一个问题,我建议使用处理程序映射,将对象的具体类链接到执行处理的闭包。 它删除了树遍历阶段,支持直接映射。 但是,请注意,如果您为C.class设置了处理程序,则无法识别上面的对象d。

这是我的2美分,我希望他们帮助...

answered 2019-02-08T14:51:07Z

4 votes

'instanceof'实际上是一个运算符,如+或 - ,我相信它有自己的JVM字节码指令。 它应该足够快。

我不应该如果你有一个开关来测试一个对象是否是某个子类的实例,那么你的设计可能需要重新设计。 考虑将子类特定的行为向下推入子类本身。

Outlaw Programmer answered 2019-02-08T14:51:46Z

4 votes

Instanceof非常快。 它归结为一个字节码,用于类参考比较。 在循环中尝试几百万个instanceof并亲自看看。

Apocalisp answered 2019-02-08T14:52:21Z

4 votes

instanceof非常有效,因此您的表现不太可能受到影响。但是,使用大量的instanceof表明存在设计问题。

如果你可以使用xClass == String.class,这会更快。 注意:最终类不需要instanceof。

Peter Lawrey answered 2019-02-08T14:52:59Z

3 votes

很难说某个JVM如何实现实例,但在大多数情况下,对象与结构和类似,并且每个对象结构都有一个指向它是其实例的类结构的指针。 所以实际上是

if (o instanceof java.lang.String)

可能与以下C代码一样快

if (objectStruct->iAmInstanceOf == &java_lang_String_class)

假设有一个JIT编译器,并且做得不错。

考虑到这只是访问一个指针,指针指向某个偏移量,指针指向并将其与另一个指针进行比较(这与测试32位数相等基本相同),我会说实际操作可以 非常快

但它并不一定非常依赖于JVM。 但是,如果这会成为代码中的瓶颈操作,我会认为JVM实现相当差。 即使是没有JIT编译器且仅解释代码的人也应该能够在几乎任何时间内进行测试实例。

Mecki answered 2019-02-08T14:54:01Z

3 votes

InstanceOf是对面向对象设计不佳的警告。

当前的JVM确实意味着instanceOf本身并没有太大的性能担忧。 如果你发现自己经常使用它,特别是核心功能,那么可能是时候看看设计了。 重构到更好的设计的性能(和简单性/可维护性)增益将大大超过在实际的instanceOf调用上花费的任何实际处理器周期。

给出一个非常小的简单编程示例。

if (SomeObject instanceOf Integer) {

[do something]

}

if (SomeObject instanceOf Double) {

[do something different]

}

是一个糟糕的架构,更好的选择是让SomeObject成为两个子类的父类,其中每个子类重写一个方法(doSomething),因此代码看起来如下:

Someobject.doSomething();

Demian Krige answered 2019-02-08T14:55:08Z

3 votes

德米安和保罗提到了一个好点; 但是,执行代码的位置实际上取决于您希望如何使用数据...

我是小型数据对象的忠实粉丝,可以在很多方面使用。 如果您遵循覆盖(多态)方法,则您的对象只能以“单向”方式使用。

这就是模式的来源......

您可以使用双重调度(如在访问者模式中)要求每个对象“调用您”传递自身 - 这将解析对象的类型。 但是(再次)你需要一个可以用所有可能的子类型“做事”的类。

我更喜欢使用策略模式,您可以在其中为要处理的每个子类型注册策略。 像下面这样的东西。 请注意,这仅对精确类型匹配有帮助,但具有可扩展性的优势 - 第三方贡献者可以添加自己的类型和处理程序。 (这适用于像OSGi这样的动态框架,可以添加新的bundle)

希望这会激发其他一些想法......

package com.javadude.sample;

import java.util.HashMap;

import java.util.Map;

public class StrategyExample {

static class SomeCommonSuperType {}

static class SubType1 extends SomeCommonSuperType {}

static class SubType2 extends SomeCommonSuperType {}

static class SubType3 extends SomeCommonSuperType {}

static interface Handler {

Object handle(T object);

}

static class HandlerMap {

private Map, Handler extends SomeCommonSuperType>> handlers_ =

new HashMap, Handler extends SomeCommonSuperType>>();

public void add(Class c, Handler handler) {

handlers_.put(c, handler);

}

@SuppressWarnings("unchecked")

public Object handle(T o) {

return ((Handler) handlers_.get(o.getClass())).handle(o);

}

}

public static void main(String[] args) {

HandlerMap handlerMap = new HandlerMap();

handlerMap.add(SubType1.class, new Handler() {

@Override public Object handle(SubType1 object) {

System.out.println("Handling SubType1");

return null;

} });

handlerMap.add(SubType2.class, new Handler() {

@Override public Object handle(SubType2 object) {

System.out.println("Handling SubType2");

return null;

} });

handlerMap.add(SubType3.class, new Handler() {

@Override public Object handle(SubType3 object) {

System.out.println("Handling SubType3");

return null;

} });

SubType1 subType1 = new SubType1();

handlerMap.handle(subType1);

SubType2 subType2 = new SubType2();

handlerMap.handle(subType2);

SubType3 subType3 = new SubType3();

handlerMap.handle(subType3);

}

}

Scott Stanchfield answered 2019-02-08T14:56:18Z

2 votes

通常,“instanceof”运算符在类似情况下(其中instanceof正在检查此基类的子类)不赞成的原因是因为您应该做的是将操作移动到方法中并将其覆盖为适当的子类。 例如,如果你有:

if (o instanceof Class1)

doThis();

else if (o instanceof Class2)

doThat();

//...

你可以用它替换它

o.doEverything();

然后在Class1调用“doThis()”中执行“doEverything()”,在Class2调用“doThat()”中执行,依此类推。

Paul Tomblin answered 2019-02-08T14:57:12Z

2 votes

在现代Java版本中,instanceof运算符作为简单的方法调用更快。 这意味着:

if(a instanceof AnyObject){

}

比较快:

if(a.getType() == XYZ){

}

另一件事是如果你需要级联许多instanceof。 然后只调用一次getType()的开关更快。

Horcrux7 answered 2019-02-08T14:57:58Z

2 votes

我将以性能为例回复您。 但是,完全避免问题(或缺少问题)的方法是为您需要执行instanceof的所有子类创建父接口。 接口将是子类中所有方法的超级集合,您需要对其进行instanceof检查。 如果方法不适用于特定的子类,只需提供此方法的虚拟实现。 如果我没有误解这个问题,这就是我过去常常遇到的问题。

Jose Quijada answered 2019-02-08T14:58:28Z

1 votes

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

Salix alba answered 2019-02-08T14:59:15Z

0 votes

如果它确实是项目中的性能问题,您应该测量/分析。 如果是的话,我建议重新设计 - 如果可能的话。 我很确定你无法击败平台的原生实现(用C语言编写)。 在这种情况下,您还应该考虑多重继承。

你应该告诉更多关于这个问题的信息,也许你可以使用一个关联存储,例如 地图&lt;类,对象&gt; 如果你只对具体类型感兴趣。

Karl answered 2019-02-08T14:59:53Z

0 votes

关于Peter Lawrey的注释,你不需要最终类的instanceof,只能使用引用相等,小心! 即使最终的类无法扩展,也不能保证它们不会被同一个类加载器加载。 只有使用x.getClass()== SomeFinal.class或它的同类,如果你绝对肯定只有一个类加载器正在代码中。

answered 2019-02-08T15:00:46Z

0 votes

我也更喜欢枚举方法,但我会使用抽象基类来强制子类实现getType()方法。

public abstract class Base

{

protected enum TYPE

{

DERIVED_A, DERIVED_B

}

public abstract TYPE getType();

class DerivedA extends Base

{

@Override

public TYPE getType()

{

return TYPE.DERIVED_A;

}

}

class DerivedB extends Base

{

@Override

public TYPE getType()

{

return TYPE.DERIVED_B;

}

}

}

mike answered 2019-02-08T15:01:14Z

0 votes

我认为值得提交一个反例,在这个页面上的普遍共识中,“instanceof”不值得担心。 我发现我在内循环中有一些代码(在一些历史性的优化尝试中)

if (!(seq instanceof SingleItem)) {

seq = seq.head();

}

在SingleItem上调用head()返回值不变。 替换代码

seq = seq.head();

虽然在循环中发生了一些非常繁重的事情,比如字符串到双重转换,但是从269ms加速到169ms。 当然,加速更多是由于消除了条件分支而不是消除了instanceof运算符本身; 但我认为值得一提。

Michael Kay answered 2019-02-08T15:01:57Z

-3 votes

你专注于错误的事情。 instanceof和用于检查相同事物的任何其他方法之间的差异甚至可能是不可测量的。 如果性能至关重要,那么Java可能是错误的语言。 主要原因是您无法控制VM何时决定要收集垃圾,这可能会使CPU在大型程序中持续几秒钟(MagicDraw 10非常适合)。 除非您控制该程序将运行的每台计算机,否则您无法保证它将在哪个版本的JVM上运行,并且许多较旧的JVM都存在严重的速度问题。 如果它是一个小应用程序,您可以使用Java,但如果您经常阅读并丢弃数据,那么您将注意到GC启动时。

tloach answered 2019-02-08T15:02:34Z

java instanceof性能差_在J中使用instanceof的性能影响相关推荐

  1. java.lang.IllegalArgumentException: 字符[_]在域名中永远无效。 at

    [http-nio-8080-exec-1] org.apache.coyote.AbstractProcessor.parseHost [xxx_tomcat] 是无效主机注意:更多的请求解析错误将 ...

  2. floatmap 二维数组_用J中的多维数组进行Arrays.fill

    用J中的多维数组进行Arrays.fill 如何在不使用循环的情况下用Java填充多维数组? 我试过了: double[][] arr = new double[20][4]; Arrays.fill ...

  3. java osgi web开发_在Tomcat中使用Java Web应用程序的OSGi软件包

    我试图从Java Web应用程序调用OSGi包的方法.两者都应该在Tomcat 7上运行. 我已经写了一个正常的Java应用程序,它调用OSGi软件包中的方法,如本站所述:http://drupal. ...

  4. java swt 画按钮_向表中添加按钮(java swt)

    我正在尝试复制类似于此的UI: 我一直在关注如何创建表格每列中的按钮的作者说明(没有成功).我的项目与他的区别在于我正在尝试使用Tree而不是Table,而我正在使用eclipse TreeViewe ...

  5. spark 算子使用类变量_自己工作中超全spark性能优化总结

    来源:https://zhuanlan.zhihu.com/ p/108454557 作者:一块小蛋糕 编辑:深度传送门 Spark是大数据分析的利器,在工作中用到spark的地方也比较多,这篇总结是 ...

  6. java的sas数据安全_使用sas中的do循环指定数据信息

    这里有一些方法可行 . 最直接可以翻译的方法是使用宏语言 . 你需要将这两个循环翻译成这样的东西: %do i = 1940 %to 2018; %do j = 1 %to 12; informat ...

  7. java dao层代码生成器_实际开发中 dao、entity的代码怎样自动生成?一款工具送给你...

    01 关注"一猿小讲"朋友,都知道以往的文章一直倡导拒绝 CRUD,那到底什么是 CRUD?今天咱们就聊聊 Java 妹子小猿与数据库老头交互的事儿. 产品小汪铿锵有力的说:小猿同 ...

  8. Java 计算字母个数_统计字符串中每个字母的个数

    最近整理之前自己学习Java时的一些代码笔记,可能都是一些比较基础的Java知识,在这里只是给需要的人参考一下. 统计一个字符串中的每个字母的个数 现有字符串:dludsstytrwtrjahtksd ...

  9. java redis 批量删除_在Spring中使用Redis Lua脚本批量删除缓存

    背景 之前分享了一篇利用lua脚本批量删除redis的key的文章.现在项目中我打算使用spring的缓存,而Spring缓存以前我是用ehcache来做实现的.没发现什么问题..这次我换成redis ...

最新文章

  1. 鸟哥的Linux私房菜(服务器)- 簡易 APT/YUM 伺服器設定
  2. mysql超经典的8小时问题-wait_timeout
  3. pipeline 流水线设计
  4. jar打包 剔除第三方依赖以及它的依赖_为什么Spring Boot的 jar 可以直接运行?
  5. Java中常用的集合
  6. Scala 入门3(类、Trait、模式匹配、正则、异常、提取器、IO)
  7. 【转载保存】webCollector使用教程
  8. js常用方法,JS实用方法,jq获得文件后缀,解析window。location,解析URL参数
  9. Arthas简单入门与初步实践
  10. 【技术贴】Please verify that the Microsoft Data Access
  11. 绑定事件和解绑事件的方法
  12. Show一下2008新技术体验活动的奖品
  13. 政企数字化转型怎么做?先从华为云WeLink “云签约”打个样儿
  14. char在计算机中是什么作用,C语言中char的用法
  15. 公交车查询系统软件测试,公交APP评测:谁是最好用的公交线路查询软件?
  16. linux 查看磁盘寿命,如何确定Linux下SSD的写周期数或预期寿命?
  17. Myflight航班查询系统
  18. 练习篇:完整实践——实现一个简易日记本应用
  19. 基于springboot的生鲜门店配送管理系统(idea+springboot+html+thymeleaf)
  20. github.io网页无法打开(连接不是私密连接)

热门文章

  1. 程序员 | 冬天到了,先储备点“粮食”
  2. “野蛮生长”的区块链是机遇还是泡沫?
  3. ABAP: 今天是星期几?
  4. [飞桨机器学习]决策树算法
  5. 上海贝尔RG100A-CA无线猫破解教程
  6. 悼念乔布斯---他的产品改变了世界,思想影响了一代人,三个故事,勉励大家
  7. 用Matlab实现车牌分割(可识别大部分蓝色、绿色车牌)
  8. java面试、笔试题(0815版)
  9. 致不可重新来过一次的青春(下)
  10. js设置input不可编辑