spring框架源代码分析-反射

反射获取注解:

上下文获取注解的代码:

getBeansWithAnnotation源码跟踪,找到真正调用,做了反射缓存:

结论:jdk 的java.lang.Class 反射获取,效率还好;

反射获取方法,invoke执行方法:

Java 方法反射性能差主要原因是:

1.获取Method对象慢

        1.1 需要检查方法权限

        每次获取Method对象,都需要检查方法权限是否合法,哪怕是已经调用的Method对象。

        1.2 需要遍历筛选递归

        每次都需要遍历筛选才能定位获取到Method对象,有时候甚至还需要递归向父类和接口遍历筛选获取Method对象

        1.3 每一个Method都有一个root,不暴漏给外部,而是每次copy一个Method

        每次都要拷贝一个Method对象

2.调用invoke方法慢

        2.1 Method#invoke 方法会对参数做封装和解封操作

        invoke 方法的参数是 Object[] 类型,也就是说,如果方法参数是简单类型的话,需要在此转化成 Object 类型,例如 long ,在 javac compile 的时候 用了Long.valueOf() 转型,也就大量了生成了Long 的 Object, 同时 传入的参数是Object[]数值,那还需要额外封装object数组。而在上面 MethodAccessorGenerator#emitInvoke 方法里我们看到,生成的字节码时,会把参数数组拆解开来,把参数恢复到没有被 Object[] 包装前的样子,同时还要对参数做校验,这里就涉及到了解封操作。因此,在反射调用的时候,因为封装和解封,产生了额外的不必要的内存浪费,当调用次数达到一定量的时候,还会导致 GC。

        2.2 需要检查方法可见性

        每次调用都必须检查方法的可见性(在 Method.invoke 里)

        2.3 需要校验参数

        每次调用都要检查每个实际参数与形式参数的类型匹配性(在NativeMethodAccessorImpl.invoke0 里或者生成的 Java 版 MethodAccessor.invoke 里)

        2.4 invoke调用逻辑是委托给MethodAccessor的,而accessor对象会在第一次invoke的时候才创建,是一种lazy init方式

        2.5 反射方法难以内联

        内联概念:把函数调用的方法直接内嵌到方法内部,减少函数调用的次数。

        native版的反射调用则无法被有效内联,因而调用开销无法随程序的运行而降低。

        2.6 JIT 无法优化

        因为反射涉及到动态加载的类型,所以无法进行优化。

反射获取方法的优化方案:

方案一,本地反射:

import jdk.internal.reflect.MethodAccessor;
import jdk.internal.reflect.MethodAccessor;
import jdk.internal.reflect.ReflectionFactory;
import sun.misc.Unsafe;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class UnsafeExample {

    static final Unsafe unsafe;

    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        String className = "com.dblones.java.reflect.example01.Animal";
        Class clazz = Class.forName(className);
        Constructor constructor = clazz.getConstructor();
        Method method = clazz.getMethod("run", double.class);
        method.setAccessible(true);
        long noInflationOffset = unsafe.staticFieldOffset(ReflectionFactory.class.getDeclaredField("noInflation"));
        Object reflectionFactoryClass = unsafe.staticFieldBase(ReflectionFactory.class.getDeclaredField("noInflation"));
        unsafe.putBoolean(reflectionFactoryClass, noInflationOffset, true);
        long methodAccessorOffset = unsafe.objectFieldOffset(Method.class.getDeclaredField("methodAccessor"));
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        MethodAccessor methodAccessor = reflectionFactory.newMethodAccessor(method);
        unsafe.putObject(method, methodAccessorOffset, methodAccessor);
        Object obj = constructor.newInstance();
        long startTime = System.nanoTime();
        for(int i = 0; i < 10000; i++) {
            Object distance = (Object) method.invoke(obj, 2d*i);
        }
        long endTime = System.nanoTime();
        System.out.println("调用耗时:" + (endTime - startTime));
    }
}
import jdk.internal.reflect.MethodAccessor;
import jdk.internal.reflect.ReflectionFactory;
import sun.misc.Unsafe;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class UnsafeExample {

    static final Unsafe unsafe;

    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        String className = "com.dblones.java.reflect.example01.Animal";
        Class clazz = Class.forName(className);
        Constructor constructor = clazz.getConstructor();
        Method method = clazz.getMethod("run", double.class);
        method.setAccessible(true);
        long noInflationOffset = unsafe.staticFieldOffset(ReflectionFactory.class.getDeclaredField("noInflation"));
        Object reflectionFactoryClass = unsafe.staticFieldBase(ReflectionFactory.class.getDeclaredField("noInflation"));
        unsafe.putBoolean(reflectionFactoryClass, noInflationOffset, true);
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        MethodAccessor methodAccessor = reflectionFactory.newMethodAccessor(method);
        Object obj = constructor.newInstance();
        long startTime = System.nanoTime();
        for(int i = 0; i < 10000; i++) {
            Object distance = (Object) methodAccessor.invoke(obj, new Object[]{2d*i});
        }
        long endTime = System.nanoTime();
        System.out.println("调用耗时:" + (endTime - startTime));
    }
}

方案二,三方反射: 

import com.esotericsoftware.reflectasm.MethodAccess;
import com.esotericsoftware.reflectasm.MethodAccess;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class AsmReflectExample {

    public static void main(String[] args) throws Throwable {
        String className = "com.dblones.java.reflect.example01.Animal";
        Class clazz = Class.forName(className);
        Constructor constructor = clazz.getConstructor();
        MethodAccess access = MethodAccess.get(clazz);
        Integer index = access.getIndex("run", double.class);
        Object obj = constructor.newInstance();
        long startTime = System.nanoTime();
        for(int i = 0; i < 10000; i++) {
            Object distance = access.invoke(obj, index, 2d*i);
        }
        long endTime = System.nanoTime();
        System.out.println("调用耗时:" + (endTime - startTime));
    }
} 

既然这样,咱们来看看JDK 1.8.0_331的反射代码,是不是实现了方案一的优化,sun公司已经优化了:

最后,结论是,可以直接放心的写反射代码了:

 

 

性能是有损失,差多少的问题。

Java反射性能分析及优化 - 哔哩哔哩一说到反射,我们经常听到”反射调用慢”,”反射性能差,尽量少使用”这类言论,那我们有没有想过反射性能差,那到底差多少呢?性能差原因是什么?有没有优化的空间呢?1.Java反射性能到底差多少?有比较才有鉴别,我们说反射性能差,其实是相对的,是相对于直接调用而言的。那我们分别写个直接调用和反射的例子来比较一下,反射的性能比直接调用到底差多少?public class Animal { public double run(double duration){ double distahttps://www.bilibili.com/read/cv13256282 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值