[关闭]
@ghimi 2018-10-20T12:41:06.000000Z 字数 2449 阅读 1414

Java 反射的性能问题

Java 反射


Java 反射的性能问题

Java 反射效率到底如何,花了点时间,做了一个简单的测试.供大家参考.

测试背景

  1. 测试简单 Bean (int,Integer,String) 的 set 方法
  2. loop 一亿次
  3. 测试代码尽可能避免对象的创建,复发方法的调用,仅仅测试 set 方法的耗时

测试结果

场景 本机测试结果(xp,双核,2G) 服务器测试结果(Linux,XEN虚拟机,8核)
方法直接调用 235ms 190ms
jdk Method调用 29188ms 4633ms
jdk Method调用(稍作优化) 5672ms 4262ms
Cglib FastMethod调用 5390ms 2787ms

得出一个感性的结果

  1. jdk 反射效率是直接调用的一个数量级,差不多 20 倍
  2. 一个 set 方法的反射调用时间 = 4633 ms/1亿/3次 = 0.0154us
  3. Cglib 的fastmethod 还是有优势的

当然反射不止一种方法的,而且也有一些比较常见的优化方式.我们将会测试一下:

  1. 直接访问的耗时
  2. 直接反射的耗时
  3. 缓存需要查找的函数反射的耗时
  4. 使用 reflectasm 的反射耗时
  1. package top.ghimi;
  2. import java.lang.reflect.InvocationTargetException;
  3. import java.lang.reflect.Method;
  4. import com.esotericsoftware.reflectasm.MethodAccess;
  5. public class ReflectionTest {
  6. public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
  7. long now;
  8. long sum = 0;
  9. TestClass t = new TestClass();
  10. now = System.currentTimeMillis();
  11. for (int i = 0; i < 500000; ++i) {
  12. t.setNum(i);
  13. sum += t.getNum();
  14. }
  15. System.out.println("get-set 耗时" + (System.currentTimeMillis() - now) + "ms秒,和是" + sum);
  16. sum = 0;
  17. now = System.currentTimeMillis();
  18. for (int i = 0; i < 50000; ++i) {
  19. Class<?> c = Class.forName("top.ghimi.TestClass");
  20. Class<?>[] argsType = new Class[1];
  21. argsType[0] = int.class;
  22. Method m = c.getMethod("setNum", argsType);
  23. m.invoke(t, i);
  24. sum += t.getNum();
  25. }
  26. System.out.println("标准反射 耗时" + (System.currentTimeMillis() - now) + "ms秒,和是" + sum);
  27. sum = 0;
  28. now = System.currentTimeMillis();
  29. Class<?> c = Class.forName("top.ghimi.TestClass");
  30. Class<?>[] argsType = new Class[1];
  31. argsType[0] = int.class;
  32. Method m = c.getMethod("setNum", argsType);
  33. for (int i = 0; i < 500000; ++i) {
  34. m.invoke(t, i);
  35. sum += t.getNum();
  36. }
  37. System.out.println("缓存反射 耗时" + (System.currentTimeMillis() - now) + "ms秒,和是" + sum);
  38. sum = 0;
  39. now = System.currentTimeMillis();
  40. MethodAccess ma = MethodAccess.get(TestClass.class);
  41. int index = ma.getIndex("setNum");
  42. for (int i = 0; i < 500000; ++i) {
  43. ma.invoke(t, index, i);
  44. sum += t.getNum();
  45. }
  46. System.out.println("reflectasm反射 耗时" + (System.currentTimeMillis() - now) + "ms秒,和是" + sum);
  47. }
  48. }

运行结果如下:

  1. -------------------------------------------------------
  2. T E S T S
  3. -------------------------------------------------------
  4. Running top.ghimi.AppTest
  5. get-set 耗时20ms秒,和是124999750000
  6. 标准反射 耗时107ms秒,和是1249975000
  7. 缓存反射 耗时15ms秒,和是124999750000
  8. reflectasm反射 耗时30ms秒,和是124999750000
  9. Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.184 sec
  10. Results :
  11. Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

从结果可以看出,标准反射的运行时间是最长的,比较以外的是缓存反射的运行时间要比正常使用 get-set 方法耗时要短,可见反射并非在任何时候都是消耗性能的.
可以看出,查找函数依然是耗时最长的部分,JDK7的优化确实很不错,由JDK6的40倍降到10倍左右,reflectasm invoke的效率比java原生invoke好,大致是直接访问的4倍时间。效率确实可以一用。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注