2008-07-20
提高javaReflecton的性能
我最近一段时间在研究java字节码,弄出来两个东西,
一个是ioc框架
http://code.google.com/p/pxbioc
另一个是今天要发的FastReflection
http://code.google.com/p/fast-reflection/
假设我们有这样一个类
我们现在要为一个构造一个Test类的实例,并且把他的id设置为一
用java Reflection API,我们可以这样
如果只构造一次那点时间可以忽略,但是如果构造100,000,000呢?
这个代价有点大了
现在我们有了FastReflection
速度就快多了,[在我的另一个测试里面FastReflectionMethod所花的时间是JavaReflectionAPI的10%左右,见附件]
为什么呢?
其实在FastReflection中我定义了一个接口
如果Test.setId()方法对应的一个FastMethod实现为
那我们只要构造一个 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的全部功能
一个是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的全部功能
评论
pxb1988
2008-09-01
fireflyc 写道
如果是做反射的话那么jdk5做反射的时候效率和直接执行这个方法的效率几乎是等价的。
而且即使有差异这点差异几乎可以忽略的,有时候你甚至用stringbuffer代替string就能换回这个时间了。
唯一的问题是在对注解进行反射的时候效率就很低了。不过注解来代替部分配置文件还不是照样大行其道?
所以片面的追求一个系统快上0.3333ms是没有意义的。
而且即使有差异这点差异几乎可以忽略的,有时候你甚至用stringbuffer代替string就能换回这个时间了。
唯一的问题是在对注解进行反射的时候效率就很低了。不过注解来代替部分配置文件还不是照样大行其道?
所以片面的追求一个系统快上0.3333ms是没有意义的。
我的测试就是在jdk1.5下进行的,代码生成方法所用的时间大约是直接反射的10~20%.
用stringbuffer代替string是好的,
如果再用上cglib代替直接反射就是好上加好.
0.3333ms并不是没有意义,而是优势,优势一次是体现不出来的,而是靠N次.
N越大,优势越大
pxb1988
2008-09-01
ajoo 写道
cglib只有FastMethod,FastConstructor,还没有FastField。你要是写一个FastField,估计肯定会有人用的。
访问field可以用一对getter和setter代替,直接访问好像是不推荐的.
我已经写了FastField在原文的下载里面.
fireflyc
2008-08-26
如果是做反射的话那么jdk5做反射的时候效率和直接执行这个方法的效率几乎是等价的。
而且即使有差异这点差异几乎可以忽略的,有时候你甚至用stringbuffer代替string就能换回这个时间了。
唯一的问题是在对注解进行反射的时候效率就很低了。不过注解来代替部分配置文件还不是照样大行其道?
所以片面的追求一个系统快上0.3333ms是没有意义的。
而且即使有差异这点差异几乎可以忽略的,有时候你甚至用stringbuffer代替string就能换回这个时间了。
唯一的问题是在对注解进行反射的时候效率就很低了。不过注解来代替部分配置文件还不是照样大行其道?
所以片面的追求一个系统快上0.3333ms是没有意义的。
ajoo
2008-08-26
cglib只有FastMethod,FastConstructor,还没有FastField。你要是写一个FastField,估计肯定会有人用的。
ajoo
2008-08-21
其实,switch语句的效率开销基本可以忽略吧?估计当你得到一个FastMethod的时候,cglib已经把方法编号计算好了,所以这个方法编号解析也不在N之内。
reflection的一个另外的问题,在于boxing。这个恐怕没有什么好办法来加速。:)
reflection的一个另外的问题,在于boxing。这个恐怕没有什么好办法来加速。:)
pxb1988
2008-08-21
starfeng 写道
不知道你的这个和cglib的fastmethod比, 有什么优势,或不同.
我看了一下cglib的代码,发现我有点重复发明轮子了,应为cglib足已实现fastmethod功能了
现在我对我这个和cglib做一个比较,为了说明方便把'我的'用字母p表示,cglib用字母c表示,我们要调用类A中的方法实现快递调用.
1.功能
p的功能是c功能的一个子集
2.使用是否方便
p和c差不多
3.占用内存
假设一个类有n个方法,p将生成n个类,c只生成一个类.如果n够大,p占用的比较多
4.性能
如果持续调用同一个方法(方法名,参数类型都一样),p将比c快.
原因p只对A中的一个方法生成对应的类,而c用一个类完成了A中所以的方法调用.换一种说法就是p生成的一个方法只调用A的一个目标方法,而c生成的一个方法能调用所有A中的方法.p,c生成的代码如下
p生成的代码
Object invoke(Object o,Object ...args)
{
return ((A)o).foo(args[0],args[1]...);
}
c生成的代码
//id为方法对应的编号
Object invoke(int id,Object o,Object ...args)
{
switch(id)
{
case 0:return ((A)o).foo(args[0],args[1]...);
case 1:return ((A)o).bar(args[0],args[1]...);
....;
}
}
显然,c在调用之前要获取方法编号,调用的还有对id进行判断,相对于p的直接调用,p有优势.
全是自己的观点,说的可能有点不对,希望大家多多指教.
starfeng
2008-08-21
不知道你的这个和cglib的fastmethod比, 有什么优势,或不同.
titanfoot
2008-07-24
不错,后生可畏阿
shellkk
2008-07-22
支持一下,不过好像性能不是10%,而是19%左右
quaff
2008-07-22
pxb1988 写道
quaff 写道
楼主的想法不错,但是每个方法和构造参数都要生成一个类,方法太多的话perm内存空间很容易被塞满.hibernate spring之类的只是对每个类生成一个子类,方法的数量和类的数量不是一个级别的,因此启动的时候需要指定大一点的perm空间.
还有个疑问既然这样可以做反射优化,为什么jdk jvm不能做这样的事情?
还有个疑问既然这样可以做反射优化,为什么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单独做优化,只要对外的接口保持一致就行了.
pxb1988
2008-07-21
lsy 写道
的确,这是一个解决性能的问题。不过用例不会太多,因为众所周之,反射的性能开销是很大的,碰到LZ提到的这种情况,都会想办法绕过Reflaction API去解决,而FastMethod可以说是一个方案。
PS:楼主的想法,能力,对事的态度都比较欣赏,再接再厉哈。
PS:楼主的想法,能力,对事的态度都比较欣赏,再接再厉哈。
我一定会努力的,呵呵
pxb1988
2008-07-21
quaff 写道
楼主的想法不错,但是每个方法和构造参数都要生成一个类,方法太多的话perm内存空间很容易被塞满.hibernate spring之类的只是对每个类生成一个子类,方法的数量和类的数量不是一个级别的,因此启动的时候需要指定大一点的perm空间.
还有个疑问既然这样可以做反射优化,为什么jdk jvm不能做这样的事情?
还有个疑问既然这样可以做反射优化,为什么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好一点,我比较喜欢,呵呵
quaff
2008-07-21
楼主的想法不错,但是每个方法和构造参数都要生成一个类,方法太多的话perm内存空间很容易被塞满.hibernate spring之类的只是对每个类生成一个子类,方法的数量和类的数量不是一个级别的,因此启动的时候需要指定大一点的perm空间.
还有个疑问既然这样可以做反射优化,为什么jdk jvm不能做这样的事情?
还有个疑问既然这样可以做反射优化,为什么jdk jvm不能做这样的事情?
lsy
2008-07-21
的确,这是一个解决性能的问题。不过用例不会太多,因为众所周之,反射的性能开销是很大的,碰到LZ提到的这种情况,都会想办法绕过Reflaction API去解决,而FastMethod可以说是一个方案。
PS:楼主的想法,能力,对事的态度都比较欣赏,再接再厉哈。
PS:楼主的想法,能力,对事的态度都比较欣赏,再接再厉哈。
pxb1988
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.我是学生,没有项目开发经验,表达能力很差,可能没有表达清楚,请多多指教
lsy
2008-07-21
从你的分析上看来区别就在于标准method的反射API的invoke方法被FastMethod重写了。那么这样一来,其实上也就不算是反射了,因为FastMethod是实实在在的知道Test有setId的这么一个方法,但是往往反射的用途是通过Class和一个方法名去找到一个需要执行的方法运行,如果找不到那么也可以通过捕获异常作处理,这对构建系统是很有帮助的;而不是明确的知道一个肯定存在的方法,然后N次执行它,如果是这样的话,那么也没反射什么事了。
发表评论
提醒: 该博客已发表在公共论坛,博客所有留言会成为论坛回贴,留言请注意遵守论坛发贴规则
- 浏览: 115 次
- 性别:

- 来自: 杭州

- 详细资料
搜索本博客
最近加入圈子
最新评论
-
提高javaReflecton的性能
fireflyc 写道如果是做反射的话那么jdk5做反射的时候效率和直接执行这个 ...
-- by pxb1988 -
提高javaReflecton的性能
ajoo 写道cglib只有FastMethod,FastConstructor ...
-- by pxb1988 -
提高javaReflecton的性能
如果是做反射的话那么jdk5做反射的时候效率和直接执行这个方法的效率几乎是等价的 ...
-- by fireflyc -
提高javaReflecton的性能
cglib只有FastMethod,FastConstructor,还没有Fas ...
-- by ajoo -
提高javaReflecton的性能
其实,switch语句的效率开销基本可以忽略吧?估计当你得到一个FastMeth ...
-- by ajoo






评论排行榜