精华帖 (9) :: 良好帖 (6) :: 新手帖 (3) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-01-12
最后修改:2009-03-02
http://www.iteye.com/topic/336082
最新添加(2009-3-1),纠正文章错误
引用 真心接受批评,并感谢 sdh5724 指正。
好久没看这个贴了,用 sdh5724 提醒的方式 beanCopier 复制,效率很高,是标准Java反射调用的 1/5. 10000次对象复制测试结果(ms): task 直接复制 31 task java标准反射复制 610 task cglib Fast复制(不好的写法) 890 task cglib beancopier复制 125 原帖 1. 背景 大家都知道,Java的反射调用性能要远低于直接对象调用,一般慢10-20倍多,但很多地方反射调用又是必须的,例如:BeanUtils.copyValues,ORM框架底层等对象映射创建等。因此有的开源框架通过asm等代码生成方式来优化Java反射调用的性能。 cglib中提供了Fast反射功能,包括FastClass,FastMethod等,替代 Java的标准实现,以图提高性能。 2. 问题 但我在实测中发现,cglib的Fast反射框架性能反而不如 JDK的标准反射实现. 测试用例:对ManageLogVO的两个实例进行copyValue,其中第一个是标准方式创建和复制,第二个通过反射方式创建和赋值。 分别采用 Java直接对象操作: testCopyValue(); JDK标准反射调用, testCopyValueUseReflection(); Cglib Fast反射: testCopyValueUseCglib(); 执行10000次,尽量模拟真实环境情况。 3. 测试结果 下面是实测结果,记录执行10000次总耗用时间: testCopyValue 31ms testCopyValueUseReflection 609ms testCopyValueUseCglib 922ms 反射耗时是直接操作的20倍,Fast反射更是对象直接操作的30倍。 4 代码片段: 1) 直接操作(共8个属性操作,限于篇幅只列出2个): ManagelogVO newVO = new ManagelogVO(); newVO.setLogid(managelogVO.getLogid()); newVO.setOprcode(managelogVO.getOprcode()); 2) JDK 反射 Object o = null; try { Class clazz = Class.forName("demo.cglib.ManagelogVO"); o = clazz.newInstance(); Method setterMethod = null; Method getterMethod = null; Object v = null; setterMethod = clazz.getMethod("setLogid", new Class[]{Long.class}); getterMethod = clazz.getMethod("getLogid", null); v = getterMethod.invoke(managelogVO, null); setterMethod.invoke(o, new Object[]{v}); setterMethod = clazz.getMethod("setOprcode", new Class[]{String.class}); getterMethod = clazz.getMethod("getOprcode", null); v = getterMethod.invoke(managelogVO, null); setterMethod.invoke(o, new Object[]{v}); 3) Cglib Fast反射 Object o = null; try { Class clazz0 = Class.forName("demo.cglib.ManagelogVO"); // FastClass clazz = FastClass.create(clazz0); o = clazz.newInstance(); FastMethod setterMethod = null; FastMethod getterMethod = null; Object v = null; setterMethod = clazz.getMethod("setLogid", new Class[]{Long.class}); getterMethod = clazz.getMethod("getLogid", null); v = getterMethod.invoke(managelogVO, new Object[]{}); setterMethod.invoke(o, new Object[]{v}); setterMethod = clazz.getMethod("setOprcode", new Class[]{String.class}); getterMethod = clazz.getMethod("getOprcode", null); v = getterMethod.invoke(managelogVO, new Object[]{}); setterMethod.invoke(o, new Object[]{v}); 通过JProfiler分析,结果类似,并且可以发现 FastClass.getMethod(), FastClass.create(),FastMethod.invoke() 分别是耗时大户,造成性能低下。 5 优化建议: 我的测试用例,基本模拟了真实ORM框架下的一些操作。instance每次创建,method每次都要查找,而由于 getMethod是性能消耗的主要地方,因此,可以考虑对Method做缓存处理,无论对于 JDK反射还是 Cglib fast反射都有一倍以上的性能提高。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-01-12
我的测试结果是JDK反射getMethod耗时是method.invoke的10倍以上,method.invoke耗时是直接调用的100倍以上,method.invoke耗时是fastMethod的2倍不到,也就是说fastMethod比直接反射要快一些,但如果直接反射不作检查的话性能会提高一倍,比fastMethod要快一点
|
|
返回顶楼 | |
发表时间:2009-01-12
getMethod过程中,会构建大量对象,加剧gc,因此缓存Method会有较明显的效果。
|
|
返回顶楼 | |
发表时间:2009-01-12
cglib慢在构造fastclass
java实现慢在getMethod和检查 |
|
返回顶楼 | |
发表时间:2009-01-12
不知大家有没有搜罗更高效的解决办法。
|
|
返回顶楼 | |
发表时间:2009-01-12
最后修改:2009-01-12
都像你这么写代码, 还有什么比较地位, CGLIB根本不是这么用的。 CGLIB创建对象是非常消耗时间的。 在一个应用服务器里, 基本是创建一次class, 怎么会像你这样, 把把都是CREATE一个。
求求大家, 别把业界公认的高速度的东西写不规范的代码来比较。 做性能测试有很多前提的, 不能乱CALL API。 要遵循人家的API使用规则。 而且, 对象COPY根本不是这么写的, BeanCopier : class MyBeanCopy{ static BeanCopier copy = BeanCopier.create(Bean.class, Bean2.class, false); void beanCopies(Object source , Object target){ copy.copy(source, target, null); } } |
|
返回顶楼 | |
发表时间:2009-01-12
对象不是有个clone的功能么
|
|
返回顶楼 | |
发表时间:2009-01-13
sdh5724 写道 都像你这么写代码, 还有什么比较地位, CGLIB根本不是这么用的。 CGLIB创建对象是非常消耗时间的。 在一个应用服务器里, 基本是创建一次class, 怎么会像你这样, 把把都是CREATE一个。
求求大家, 别把业界公认的高速度的东西写不规范的代码来比较。 做性能测试有很多前提的, 不能乱CALL API。 要遵循人家的API使用规则。 而且, 对象COPY根本不是这么写的, BeanCopier : class MyBeanCopy{ static BeanCopier copy = BeanCopier.create(Bean.class, Bean2.class, false); void beanCopies(Object source , Object target){ copy.copy(source, target, null); } } 多谢提醒。java bean值拷贝时你的方法不错,稍后我也测一下。 不知一般的反射调用,比如:加载配置文件中的类和值定义,初始化到内存对象中,有没有更好的办法(cglib我还没挖掘透,呵呵),就像你提到的 BeanCopier, ps 我指的是一般意义上比较基础的反射操作。 |
|
返回顶楼 | |
发表时间:2009-02-17
很多中间对象需要你的CACHE的, 不能像你这么写代码。这么比没有什么意义。 CGLIB还有功能是为了实现AOP, 这个东西出现在JDKPROXY之前, 无所谓睡优秀的问题。
|
|
返回顶楼 | |
发表时间:2009-02-19
在数据对象的反射上我使用的是field的setValue方法,这样的速度我测试过一般数据对象可以达到每秒反射4万次以上,可以达到压力测试的要求,但是beanUtil的反射慢多了,但是功能相对来说很强大
|
|
返回顶楼 | |