反射性能:JVM中的高质量字节代码

abi*_*rai 8 java reflection coding-style bytecode

编辑2:具有完全object-oriented实现的程序是否具有高性能?大多数framework都是用它的全部功能编写的.然而,reflection也被大量用于实现它,因为AOPdependency injection.使用reflection会在一定程度上影响性能.

那么,使用它是好的做法reflection吗?是否有一些替代编程语言结构的反射?应该reflection在多大程度上使用?

Xye*_*ene 9

反思本身和本质上都很慢.有关详细信息,请参阅此问题.这是由几个原因造成的.Jon Skeet很好地解释了这一点:

检查是否存在无参数构造函数检查无参数构造函数的可访问性检查调用者是否有权使用反射完成(在执行时)需要分配多少空间调用构造函数代码(因为它不知道事先建构者是空的)

基本上,反射必须在调用之前执行上述所有步骤,而正常的方法调用必须做得少得多.

用于实例化B的JITted代码非常轻量级.基本上它需要分配足够的内存(除非需要GC,否则只是递增指针)而且就是这样 - 没有构造函数代码可以真正调用; 我不知道JIT是否会跳过它,但无论哪种方式都没有.

话虽如此,有很多情况下Java不够动态,无法做到你想要的东西,而反射提供了一个简单而干净的选择.请考虑以下情形:

  1. 你有大量的代表各种项目,即一个班Car,BoatHouse.
  2. 它们都扩展/实现了同一个类:LifeItem.
  3. 您的用户输入3个字符串中的一个,"Car","Boat"或"House".
  4. 您的目标是基于参数访问LifeItem的方法.
  5. 首先想到的方法是构建一个if/else结构,并构建想要的结构LifeItem.但是,这不是很可扩展,一旦你有几十个LifeItem实现,就会变得非常混乱.

反射可以在这里提供帮助:它可以用于LifeItem基于名称动态构造对象,因此"Car"输入将被分派到Car构造函数.突然之间,数百行if/else代码变成了一条简单的反射线.由于引入了带有Strings的switch语句,后一种情况在Java 7+平台上不会有效,但即便如此,我还是要避免使用包含数百种情况的交换机.以下是大多数情况下清洁度之间的区别:

没有反思:

public static void main(String[] args) {
   String input = args[0];
   if(input.equals("Car"))
      doSomething(new Car(args[1]));
   else if(input.equals("Boat"))
      doSomething(new Boat(args[1]));
   else if (input.equals("House"))
      doSomething(new House(args[1]));
   ... // Possibly dozens more if/else statements
}
Run Code Online (Sandbox Code Playgroud)

而利用反射,它可能会变成:

public static void main(String[] args) {    
   String input = args[0];
   try {
      doSomething((LifeItem)Class.forName(input).getConstructor(String.class).newInstance(args[1]));
   } catch (Exception ie) {
      System.err.println("Invalid input: " + input);
   }  
}
Run Code Online (Sandbox Code Playgroud)

我个人认为后者比第一个更整洁,更简洁,更易于维护.最后它是个人偏好,但这只是反射有用的众多案例之一.

此外,在使用反射时,您应尝试缓存尽可能多的信息.换句话说,使用简单的逻辑事物,比如不能get(Declared)Method在任何地方调用,如果你可以帮助它:而是将它存储在一个变量中,这样你就不会有任何想要使用它时重新获取引用的开销.

所以这些是赞成者和反思的两个极端.总而言之,如果反射提高了代码的可读性(就像在呈现的场景中那样),那么一定要去实现它.如果你这样做,只要考虑减少get*反射调用的次数:那些是最容易修改的.


Jav*_*ier 7

虽然反思比"传统代码"更昂贵,但过早优化是万恶之源.从长达十年的经验证据来看,我认为通过反射调用的方法几乎不会影响性能,除非它是从一个重循环中调用的,即使如此,反射也会有一些性能增强:

某些反射操作,特别是Field,Method.invoke(),Constructor.newInstance()和Class.newInstance(),已经被重写以获得更高的性能.反射调用和实例化比以前的版本快几倍 J2SDK 1.4中的增强功能 -

请注意,上面没有提到方法查找(即Class.getMethod),选择正确的Method对象通常需要额外的步骤,例如遍历类层次结构,同时在不公开的情况下请求"声明的方法",所以我倾向于在可能的情况下将找到的方法保存在合适的映射中,以便下次成本只是Map.get()和Method.invoke()的成本.我想任何编写良好的框架都可以正确处理.

还应该考虑如果使用反射(例如方法内联或转义分析,Java HotSpot™虚拟机性能增强),则无法进行某些优化.但这并不意味着必须不惜一切代价避免反思.

但是,我认为使用反射的决定应该基于其他标准,例如代码可读性,可维护性,设计实践等.在您自己的代码中使用反射时(而不是使用内部使用反射的框架),冒险将编译时错误转换为运行时错误,这些错误很难调试.在某些情况下,可以通过传统的OOP模式(如Command或Abstract Factory)替换反射调用.