Java反射优化

公司转正答辩的时候被问到,在利用反射的时候,如果并发量比较大的情况下,如何进行优化,当时比较紧张,大脑一片空白,时候回来查找了一些资料,在这里做一些总结

首先先了解一下,java的反射为什么慢
反射Field/get

跟着源码看下,最后是使用FieldAccessor来获取的:
这里写图片描述

都是写使用Unsafe类来访问的,Unsafe类是Java中可以像C语言中那样使用指针偏移来操作Java对象(还有并发CAS等)的一个工具类,这个类的实现是JNI的C++代码:

jlong  
sun::misc::Unsafe::getLong (jobject obj, jlong offset)  
{  
  jlong *addr = (jlong *) ((char *) obj + offset);  
  spinlock lock;  
  return *addr;  
}  

C++中其实就是简单的通过基地址和偏移来指针运算拿到内存值,感觉上没有什么劣势,只有JNI,但Unsafe可是JVM自带的JNI(Intrinsic function?),性能应该不会差。
但是JNI毕竟是JNI,这让JVM无法预知它的行为带来的影响,本来可以有的很多优化被此JNI调用给隔绝了,而Java本来就是靠动态优化吃饭的(Java是门半编译型半解释型语言,不像C++靠编译优化),所以性能影响还是蛮大的。

所以:

1.由于是本地方法调用,让JVM无法优化(还有JIT?)。

2.反射方法调用还有验证过程和参数问题,参数需要装箱拆箱、需要组装成Object[]形式、异常的包装等等问题,篇幅问题这里不加以叙述。

那么针对以上可以有以下的优化方案:

1、setAccessible(true)
使用了method.setAccessible(true)后 性能有了20倍的提升,实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问,由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的

JDK API中的解释 :

AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获得字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。
在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。

2、jit,不去了解反射优化,还真不知道JIT是什么东西

如果你在jdk6上跑,且如果你反射的目标方法是getter/setter methods的话,记得加上配置:-XX:-UseFastEmptyMethods -XX:-UseFastAccessorMethods , 这两个配置的关闭是为了让accessor methods能够被jit; jdk7以上不需要设置这两个配置
即时编译,又译及时编译、实时编译,动态编译的一种形式,是一种提高程序运行效率的方法。通常,程序有两种运行方式:静态编译与动态直译。静态编译的程序在执行前全部被翻译为机器码,而直译执行的则是一句一句边运行边翻译。 即时编译器则混合了这二者,一句一句编译源代码,但是会将翻译过的代码缓存起来以降低性能损耗。相对于静态编译代码,即时编译的代码可以处理延迟绑定并增强安全性。 即时编译器有两种类型,一是字节码翻译,二是动态编译翻译。 微软的.NET Framework,还有绝大多数的Java实现,都依赖即时翻译以提供高速的代码执行

如果你在jdk6上跑,且如果你反射的目标方法是getter/setter methods的话,记得加上配置:-XX:-UseFastEmptyMethods -XX:-UseFastAccessorMethods , 这两个配置的关闭是为了让accessor methods能够被jit; jdk7以上不需要设置这两个配置

3、缓存
这个优化是一般反射优化的基本解决方案,就是把所有经常用到的反射对象缓存起来,在下次用到的时候直接从缓存中获取
1. 系统启动阶段使用反射。
2. 将反射得到元数据保存起来,使用时,只需从内存中调用即可。
3. hotspot虚拟机会对执行次数较多的方法进行优化(例如使用jit技术)

4、使用高性能的反射类库ReflectASM
为什么ReflectASM比jdk的反射要快呢?

原理

    public class User {  
    private int id;  
    private String name;  

    public int getId() {  
        return id;  
    }  

    public void setId(int id) {  
         this.id = id;  
     }  

     public String getName() {  
         return name;  
     }  

     public void setName(String name) {  
         this.name = name;  
     }  
 }  



 public class ReflectAsmTest {  
     public static void main(String[] args) {  
         User user = new User();  
         //使用reflectasm生产User访问类  
         MethodAccess access = MethodAccess.get(User.class);  
         //invoke setName方法name值  
         access.invoke(user, "setName", "张三");  
         //invoke getName方法 获得值  
         String name = (String)access.invoke(user, "getName", null);  
         System.out.println(name);  
     }  
 } 

看了下源码,这段代码主要是通过asm(一种汇编语言)生产一个User的处理类 UserMethodAccess(这个类主要是实现了invoke方法)的ByteCode,然后获得该对象,通过上面的invoke操作user类。

        // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.  
    // Jad home page: http://www.kpdus.com/jad.html  
    // Decompiler options: packimports(3)   

    package com.johhny.ra;  

    import com.esotericsoftware.reflectasm.MethodAccess;  

    // Referenced classes of package com.johhny.ra:  
    //            User  

    public class UserMethodAccess extends MethodAccess  
    {  

        public UserMethodAccess()  
        {  
        }  

        /** 
         * 这个方法是主要是实现了MethodAccess 的抽象方法,来实现反射的功能   
         * @param obj  需要反射的对象 
         * @param i  class.getDeclaredMethods 对应方法的index 
         * @param 参数对象集合 
         * @return 
         */  
        public transient Object invoke(Object obj, int i, Object aobj[])  
        {  
            User user = (User)obj;  
            switch(i)  
            {  
            case 0: // '\0'  
                return user.getName();  

            case 1: // '\001'  
                return Integer.valueOf(user.getId());  

            case 2: // '\002'  
                user.setName((String)aobj[0]);  
                return null;  

            case 3: // '\003'  
                user.setId(((Integer)aobj[0]).intValue());  
                return null;  
            }  
            throw new IllegalArgumentException((new StringBuilder("Method not found: ")).append(i).toString());  
        }  
    }

看了UserMethodAccess源码后明白 ReflectASM 为什么会比java放射快那么多,其实就是我们的bean调用里面的方法,速度当然很快

注意:
1. MethodAccess.get()方法比较耗时的,特别是类方法比较多的时候,如果生成的反射类用到的地方比较多或者会多次调用,建议缓存下来,如果使用次数很少建议还是使用反射来完成功能

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值