|
精华帖 (6) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
|---|---|
| 作者 | 正文 |
|
最后更新时间:2008-07-20
我最近一段时间在研究java字节码,弄出来两个东西,
一个是ioc框架 http://code.google.com/p/pxbioc 另一个是今天要发的FastReflection http://code.google.com/p/fast-reflection/ 假设我们有这样一个类
public class Test
{
private int id;
public int getId()
{return id;}
public void setId(int id)
{ this.id=id;}
}
我们现在要为一个构造一个Test类的实例,并且把他的id设置为一 用java Reflection API,我们可以这样
Class testClass=Test.class;
Constructor constructor=testClass.getConstructors()[0];
Method method=testClass.getMethod("setId",new Class[] { int.class });
Test test=constructor.newInstance();
method.invoke(test,1);
如果只构造一次那点时间可以忽略,但是如果构造100,000,000呢? 这个代价有点大了 现在我们有了FastReflection
FastReflect fastReflect = new FastReflect();
Class testClass=Test.class;
Constructor constructor=testClass.getConstructors()[0];
FastConstructor fConstructor=fastReflect.getFastConstructor(constructor);
Method method=testClass.getMethod("setId",new Class[] { int.class });
FastMethod fMethod=fastReflect.getFastMethod(method);
Test test=fConstructor.newInstance();
fMethod.invoke(test,1);
速度就快多了,[在我的另一个测试里面FastReflectionMethod所花的时间是JavaReflectionAPI的10%左右,见附件] 为什么呢? 其实在FastReflection中我定义了一个接口
public interface FastMethod{
public Object invoke(Object object,Object ... args);
}
如果Test.setId()方法对应的一个FastMethod实现为
public class Test_setId_FastMethod implements FastMethod{
public Object invoke(Object object,Object ... args)
{
((Test)object).setId(args[0]);
return null;
}
}
那我们只要构造一个 Test_setId_FastMethod 类的实例, 然后调用它的invoke方法就能绕过javaReflection而用直接代码调用Test.setId() 速度肯定能够提高 至于如何得到这个 Test_setId_FastMethod 类我们有两种方法, 一种是生成一个Test_setId_FastMethod.java文件,然后调用javac编译,再加载,最后执行 另外一种是用字节码工具直接生成,跳过上一种方法的生成文件和编译部分, 因为编译要付出巨大的代价,我准备用第二种方法 字节码工具有很多javaassist,asm... 我选asm,asm的优点请大家到网上找 至于如何生成,因为代码太多了,我就不分析了,大家可以看FastReflect里的注释 希望大家多多指教 参考 用代码生成取代反射http://www.ibm.com/developerworks/cn/java/j-dyn0610/ 注 2008/09/01 cglib已经实现了Fast Reflection api的全部功能 声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
最后更新时间:2008-07-21
从你的分析上看来区别就在于标准method的反射API的invoke方法被FastMethod重写了。那么这样一来,其实上也就不算是反射了,因为FastMethod是实实在在的知道Test有setId的这么一个方法,但是往往反射的用途是通过Class和一个方法名去找到一个需要执行的方法运行,如果找不到那么也可以通过捕获异常作处理,这对构建系统是很有帮助的;而不是明确的知道一个肯定存在的方法,然后N次执行它,如果是这样的话,那么也没反射什么事了。
|
|
| 返回顶楼 | |
|
最后更新时间:2008-07-21
lsy 写道 从你的分析上看来区别就在于标准method的反射API的invoke方法被FastMethod重写了。那么这样一来,其实上也就不算是反射了,因为FastMethod是实实在在的知道Test有setId的这么一个方法,但是往往反射的用途是通过Class和一个方法名去找到一个需要执行的方法运行,如果找不到那么也可以通过捕获异常作处理,这对构建系统是很有帮助的;而不是明确的知道一个肯定存在的方法,然后N次执行它,如果是这样的话,那么也没反射什么事了。
首先我想说的是这个N的问题,我想比较的是速度,不可能运行一次就能把他们的时间大小比较出来,我试过用reflection方法和我的FastReflection方法调用一次所花费的时间都是0ms, 其次我想说的是 fastReflection和标准的反射API知道的东西一样的多,FastReflection是基于标准的反射的,是标准反射的一种扩展 。构造一个FastMethod实例,FastReflection仅仅需要一个Method这个Method是由程序员决定的 可以用type=Class.forName("somClass")加载一个实例然后用method=type.getDeclaringMethod("someMethod",...)来获取,这里的someClass,和someMethod可以是硬编码在程序里的也可以通过外部配置文件读取进来。显然如果someClass和someMethod不存在的话早就抛出异常,根本就轮不到fastReflection执行。 对于FastReflection来说 它要生成的FastMethod的模板是
public class ${typeName}_${methodName}_FastMethod implements FastMethod{
public Object invoke(Object object,Object ... args)
{
((${typeName})object).${methodName}(args[0],args[1],...); //参数不知道如何表示,但是我们可以知道参数的个数和类型。还涉及到类型转换问题,略去
return null; //如果method的返回值是void类型,否则返回调用结果
}
}
注:上面${}里面的内容需要替换,有些无法表示的略去 有了一个method,我们可以 typeName=method.getDeclearingClass().getName(); methodName=method.getName(); parameterTypes=method.getParameterTypes(): 这个method对应的FastMethod完全可以拼出来 所以FastReflection并不需要实在在的知道Test有setId的这么一个方法, 但是它制造出来的FastMethod的内部确实是知道的。 对于FastReflection来说,来一个Method,不管是什么Method,制造一个对应的FastMethod就好了,来100个就制造100个, ps.我是学生,没有项目开发经验,表达能力很差,可能没有表达清楚,请多多指教 |
|
| 返回顶楼 | |
|
最后更新时间:2008-07-21
的确,这是一个解决性能的问题。不过用例不会太多,因为众所周之,反射的性能开销是很大的,碰到LZ提到的这种情况,都会想办法绕过Reflaction API去解决,而FastMethod可以说是一个方案。
PS:楼主的想法,能力,对事的态度都比较欣赏,再接再厉哈。 |
|
| 返回顶楼 | |
|
最后更新时间:2008-07-21
楼主的想法不错,但是每个方法和构造参数都要生成一个类,方法太多的话perm内存空间很容易被塞满.hibernate spring之类的只是对每个类生成一个子类,方法的数量和类的数量不是一个级别的,因此启动的时候需要指定大一点的perm空间.
还有个疑问既然这样可以做反射优化,为什么jdk jvm不能做这样的事情? |
|
| 返回顶楼 | |
|
最后更新时间:2008-07-21
quaff 写道 楼主的想法不错,但是每个方法和构造参数都要生成一个类,方法太多的话perm内存空间很容易被塞满.hibernate spring之类的只是对每个类生成一个子类,方法的数量和类的数量不是一个级别的,因此启动的时候需要指定大一点的perm空间.
还有个疑问既然这样可以做反射优化,为什么jdk jvm不能做这样的事情? quaff说得对,FastReflection就是用空间来换时间. 如果能把FastReflection生成的类保存到Cache里或者直接存到硬盘上,这样或许可以减少内存占用吧, 还有FastReflection相对于标准的Reflection来说有很多局限性, 他只能对声明为public的方法\构造函数\成员反射. 显然jvm要得更多,jvm也需要对声明为private\protected的方法\构造函数\成员反射, 所以jvm没有用类似我这种方案来反射. 摘自参考2: The biggest problem with this (in a pure Java framework, maybe not in the context of translation to Flint) is with private fields. Clearly, if x in the Point class above is private, then the Field_Point_x::get() and set() methods will not work. This is fine, because reflection is supposed to preserve semantics of Java field access, etc. However, the currect reflection API does specify that in certain cases access control checks can be overridden. This is necessary if using reflection for, say, pickling and unpickling objects: the pickler (which is trusted somehow, maybe by having certain security permissions), needs to be able to access private data as well. 参考 1.Implementationhttp://cs-www.cs.yale.edu/homes/hamid/Java_Reflection/report/node18.html 2.Implementing Java Reflection[这一篇文章有提到类似于的方案] http://www.cs.yale.edu/homes/hamid/Java_Reflection/implrefl.html ps.jvm如何实现反射的资料网上很少,提到反射说得都是如何使用,在百度上找了很久都没有找到.最后在google上搜到了,看来还是google好一点,我比较喜欢,呵呵 |
|
| 返回顶楼 | |
|
最后更新时间:2008-07-21
lsy 写道 的确,这是一个解决性能的问题。不过用例不会太多,因为众所周之,反射的性能开销是很大的,碰到LZ提到的这种情况,都会想办法绕过Reflaction API去解决,而FastMethod可以说是一个方案。
PS:楼主的想法,能力,对事的态度都比较欣赏,再接再厉哈。 我一定会努力的,呵呵 |
|
| 返回顶楼 | |
|
最后更新时间:2008-07-22
pxb1988 写道 quaff 写道 楼主的想法不错,但是每个方法和构造参数都要生成一个类,方法太多的话perm内存空间很容易被塞满.hibernate spring之类的只是对每个类生成一个子类,方法的数量和类的数量不是一个级别的,因此启动的时候需要指定大一点的perm空间.
还有个疑问既然这样可以做反射优化,为什么jdk jvm不能做这样的事情? quaff说得对,FastReflection就是用空间来换时间. 如果能把FastReflection生成的类保存到Cache里或者直接存到硬盘上,这样或许可以减少内存占用吧, 还有FastReflection相对于标准的Reflection来说有很多局限性, 他只能对声明为public的方法\构造函数\成员反射. 显然jvm要得更多,jvm也需要对声明为private\protected的方法\构造函数\成员反射, 所以jvm没有用类似我这种方案来反射. 摘自参考2: The biggest problem with this (in a pure Java framework, maybe not in the context of translation to Flint) is with private fields. Clearly, if x in the Point class above is private, then the Field_Point_x::get() and set() methods will not work. This is fine, because reflection is supposed to preserve semantics of Java field access, etc. However, the currect reflection API does specify that in certain cases access control checks can be overridden. This is necessary if using reflection for, say, pickling and unpickling objects: the pickler (which is trusted somehow, maybe by having certain security permissions), needs to be able to access private data as well. 参考 1.Implementationhttp://cs-www.cs.yale.edu/homes/hamid/Java_Reflection/report/node18.html 2.Implementing Java Reflection[这一篇文章有提到类似于的方案] http://www.cs.yale.edu/homes/hamid/Java_Reflection/implrefl.html ps.jvm如何实现反射的资料网上很少,提到反射说得都是如何使用,在百度上找了很久都没有找到.最后在google上搜到了,看来还是google好一点,我比较喜欢,呵呵 标准的java反射可以对public单独做优化,只要对外的接口保持一致就行了. |
|
| 返回顶楼 | |
|
最后更新时间:2008-07-22
支持一下,不过好像性能不是10%,而是19%左右
|
|
| 返回顶楼 | |
|
最后更新时间:2008-07-24
不错,后生可畏阿
|
|
| 返回顶楼 | |




