反射的原理及其性能问题

85 阅读1分钟

注:本专栏文章均为本人原创,未经本人授权请勿私自转载,谢谢。

反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法

以下是测试反射性能的示例代码,由于一般在使用反射的场合中基本都是将反射类缓存的(例如 Spring 中的 singletonMap 等),所以我们没有将实例化的过程参与计时:

public class MyApplication {
    @Data
    static class Human {
        private Long id;
        private String name;
        private String gender;
​
        private void run() {
            //System.out.println("Human run");
        }
​
        private void eat() {
            //System.out.println("Human eat");
        }
    }
​
    public static void main(String[] args) throws Exception {
        // 设定总执行次数
        final int EXECUTE_COUNT = 1000000;
​
        // new 方式执行
        Human human = new Human();
        long startTime = System.currentTimeMillis(); // 开始计时
        for (int i = 0; i < EXECUTE_COUNT; i++) {
            human.run();
            human.eat();
        }
        System.out.println("New 方法耗时:" + (System.currentTimeMillis() - startTime));
​
        // 反射方式执行
        Class<?> aClass = Class.forName("com.example.demo.MyApplication$Human");
        Object obj = aClass.newInstance();
        Method runMethod = aClass.getDeclaredMethod("run");
        runMethod.setAccessible(true);
        Method eatMethod = aClass.getDeclaredMethod("eat");
        eatMethod.setAccessible(true);
        startTime = System.currentTimeMillis(); // 开始计时
        for (int i = 0; i < EXECUTE_COUNT; i++) {
            runMethod.invoke(obj);
            eatMethod.invoke(obj);
        }
        System.out.println("反射方法耗时:" + (System.currentTimeMillis() - startTime));
    }
}
# 以上程序输出:
New 方法耗时:9
反射方法耗时:15
​
# 若将创建逻辑移入 for 循环:
New 方法耗时:12
反射方法耗时:1273

可以看到,在动态创建对象时,反射确实带来了一些效率问题,但若将反射实例缓存后,两者性能差异并不大。而且这是执行了上百万次的运行时间,实际的生产中还需加入业务代码的执行时间,着电性能的差异就更显得微不足道了。

所以说,反射在动态创建对象时,会有较大的性能影响,解决方法就是将反射实例缓存