Java反射运行时性能

12

这只是一个学术性的练习(声明)。

我正在构建一款应用程序,它需要尽可能快地运行,因为它将与其他应用竞争。

我知道使用反射来声明一个类(示例如下)会比标准声明产生巨大的惩罚。

Class mDefinition = Class.forName("MySpecialClassString");
Constructor mConstructor = mDefinition.getConstructor(new Class[]{MySpecialClass.class});
myClass = (MySpecialClass) mConstructor.newInstance(this);

然而,如果我像标准方式一样使用myClass.myMethod(),在声明了myClass后,我是否也会遭受性能问题,还是和以标准方式声明类一样?

3个回答

15

在首次实例化对象时会有性能损失。一旦类被加载,与正常实例化相同,将不会再有进一步的性能损失。

更进一步地说,如果使用反射调用方法,前15次(Java默认值)将会有性能损失,之后JVM将重写反射调用,使其与静态编译调用完全相同。因此,即使是反复反射的方法调用,一旦JVM重新编译了字节码,也不会导致性能降低。

请参考以下两个链接了解更多信息:


感谢您提供如此详细的说明和额外的文档。在过去的几天里,这些真的非常有用。谢谢! - Frankie

7

一旦类被加载,你就可以放心使用了。这种开销与检查表示该类的运行时结构等相关。以标准方式调用方法应该没问题,但如果你开始按名称或签名搜索方法,那么将会产生额外的开销。


1
+1 同意 - 一次性动态查找不应该有太大的开销。 - Paul Bellora
@Chris 我一直在运行一些测试,你的假设完全正确。感谢你先前澄清了这个问题。 - Frankie

2

克里斯·汤普森的回答很到位。但是我对你的代码示例感到困惑。

这将动态加载类:

Class mDefinition = Class.forName("MySpecialClassString");

您可以使用以下代码获取一个Constructor,该构造函数需要该类的实例作为参数。请注意,您在编译时访问该类:MySpecialClass.class

Constructor mConstructor = mDefinition.getConstructor(new Class[]{MySpecialClass.class});

这是通过将“this”传递到构造函数来实例化一个名为“MySpecialClass”的类:
myClass = (MySpecialClass) mConstructor.newInstance(this);

根据构造函数参数,这是否意味着我们处于 MySpecialClass 的实例方法中?非常困惑。

编辑: 这更接近我所期望看到的内容:

Class<?> mDefinition = Class.forName("MySpecialClassString");

//constructor apparently takes this as argument
Class<?> constructorArgType = this.getClass(); //could be ThisClassName.class

Constructor<?> mConstructor = mDefinition.getConstructor(constructorArgType);

MySpecialInterface mySpecialInstance = (MySpecialInterface)mConstructor.newInstance(this);

其中MySpecialInterface是用于与您的动态加载类进行交互的接口:

interface MySpecialInterface {
    //methods used to interface with dynamically loaded classes
}

无论如何,请让我知道如果我误解或离题了。


你可能是对的,因为我现在才开始使用反射。让我快速向您介绍系统的工作原理。我们有三个类:ClassA_interaction、ClassB_object和ClassC_logic... ClassA_interaction启动多个ClassB_objects并将它们提供给位置。每个ClassB_object使用反射启动一个ClassC_logic。由于ClassC_logic必须调用存在于ClassB_object上的函数,因此ClassC_logic被提供了this(ClassB_object)。这可能会有点混乱,但它可以正常工作。您是否建议我采取另一种方法?感谢您抽出时间查看我的代码。 - Frankie
我必须说我个人没有使用过动态类加载,这就是为什么我想要从某人那里得到确认的原因。但是,我的理解是,如果您直接在代码中引用一个类,甚至在编译时都可以访问它,那么动态加载它就没有意义。然而,如果您有一个静态加载的Foo,它可以是抽象类或接口,并且您的动态加载类扩展/实现了Foo。这样,在您动态加载一个类并实例化它之后,您可以将其强制转换为Foo并与之交互,而无需进一步反射。 - Paul Bellora
当然,Foo 需要以抽象/接口方法声明的形式了解您将要使用这些动态加载类所需做的一切。 - Paul Bellora
动态类加载(在我的情况下)只有一个目的,即通过一个包含地图名称的文本文件来为游戏提供数据。这些地图是预先给定的,我们可以(也应该)为每个地图制定不同的策略。因此,地图的名称将决定加载哪个类。 - Frankie
实际上,您在假设策略属于常见抽象方法方面是有一定道理的。它们都扩展了一个CommonStrat()类。在运行时加载策略不是个人选择,而是我必须遵守的事情(可以正确地假设地图只会在运行时提供)。在这种情况下,对我来说最有意义的是使用反射,但我的假设可能是完全错误的。 - Frankie
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接