JAVA-提高反射效率

来源:互联网 发布:凤凰网域名注册 编辑:程序博客网 时间:2024/05/21 10:47
JAVA-提高反射效率 
功能说明: 从事java开发的都知道反射的运行速度慢,所以很多java的开发者都对反射机制的使用望而却步(包括BME组件SDO)。我想知道,究竟反射机制慢在哪里?有没有改进方法,让我们可以继续使用它?如果一个好东西因为其自身的一些缺陷而不使用它,那么实在可惜,反射也是这样。我想说的是:我们应该一点点的改进它。 
代码特点: 1. 描述
从事java开发的都知道反射的运行速度慢,所以很多java的开发者都对反射机制的使用望而却步(包括BME组件SDO)。我想知道,究竟反射机制慢在哪里?有没有改进方法,让我们可以继续使用它?如果一个好东西因为其自身的一些缺陷而不使用它,那么实在可惜,反射也是这样。我想说的是:我们应该一点点的改进它。
2. 错误的使用方法
错误的使用方法是每次需要获取Class的对象时都使用Class.forName方法,或者需要调用Class对象上的方法时都调用getDeclaredMethod(String name, Class<?>... parameterTypes)或getMethod(String name, Class<?>... parameterTypes)方法获取Method对象,再调用其上的invoke(Object obj, Object... args)方法。
这里存在两个容易造成性能损耗的地方:
Class.forName方法的调用会执行Class类文件在整个类路径下的搜索,频繁调用比较影响性能。
Class对象上的getDeclaredMethod (String, Class<?>...)或getMethod(String, Class<?>...)方法的调用会执行Method对象在Class对象上的搜索。有些同事还使用getMethods()方法获取Method数组,然后执行搜索任务,实际上getMethods()还会执行方法对象的集体Copy比直接使用(String, Class<?>...)或getMethod(String, Class<?>...)方法还要消耗时间及空间。
3. Cache思想
Cache的思想是将需要的反射中间件给存储下来,以便以后使用。不管使用什么方法获取Class对象上的Method对象,返回的都是Method对象的copy对象。这些copy对象有的只是使用一次就被回收了,未免有些可惜。我们可这以将这些对象给缓存下来,以便以后使用。而在存储数据结构中,无疑HashMap的查找速度是最快的,它主要是通过对象的Hash码进行一次查找,速度超快。但是HashMap上的操作不是线程安全的,需要改进方法实现同步。
4. 具体实现
需要两个组件ClassInfo和ReflectionCache。
ClassInfo主要保存Class对象的信息,主要是方法Map。其中ClassInfo中包括三部分方法的Map: Getter, Setter, Other。Getter是Class的属性的获取方法,Setter是Class的属性的设置方法,Other是其它方法。需要注意的是Getter和Setter的方法需要完全符合Javabean规范(isXXX方法属于Getter方法范围内),其key值是方法对应的属性名。Other方法是除Getter和Setter以外的其它方法。
ReflectionCache组件主要是通过HashMap对ClassInfo进行缓存。缓存的键值是ClassInfo中Class对象的全称。如一个String对象,它缓存的键值就是java.lang.String。并且ReflectionCache提供了几种不同get和put方法来方便用户的操作。
另外ClassInfo的生成需要用到ClassInfoUtils工具。它的主要工作是创建ClassInfo对象,其中创建ClassInfo时可以提供Method Type信息来指定缓存的方法类型(如:所有方法-All、存取器方法-Access、获取方法-Getter和设置方法-Setter)。
5. 同步控制
ReflectionCache中ClassInfoMap是一个静变量,那么随之而来的就是HashMap的同步问题。我对ReflectionCache的做了些改进,主要是对put方法的处理。首先HashMap的获取操作(get操作)没有加入同步操作,因此获取的操作是可以并发的。现在的问题在于如果获取不了ClassInfo对象时会要执行设置操作(set操作),此时并发问题随之而来。可能在同一时刻会有很多线程去设置ClassInfo,在第一个设置完ClassInfo的线程结束后,第二个线程应该停止设置ClassInfo。在此需求之上,我们需要对ReflectionCache的put操作上加上同步块,并且让put操作再执行一个额外的操作:返回添加到ClassInfoMap中的ClassInfo,不管它是不是其它线程添加的。因此我们在设置ClassInfo时,可以这样操作:
ClassInfo classInfo = ReflectionCache.putClassInfo(String.class);
6. 改进效率
改进之后的效率的提高是明显的。主要是节省了中间变量创建及反射数据的查找时间。
测试数据
100000次,20个线程,无Class.forName操作
一般用法: 11375 milliseconds
ReflectionCache: 2562 milliseconds


100000次,20个线程,有Class.forName操作
一般用法: 16125 milliseconds
ReflectionCache: 4187 milliseconds
可见使用ReflectionCache明显提高了效率。
7. 使用方法
首先导入ReflectionCache类文件或者将其打成jar包放在类路径下。
获取ClassInfo:
ClassInfo classInfo = ReflectionCache.getClassInfo(“java.lang.String”);
设置ClassInfo:
classInfo = ReflectionCache.putClassInfo(String.class);


getClassInfo和putClassInfo方法的有两种重载函数,分别对应参数是Object和Class的两种情况。 
原创粉丝点击