java有别于其他编程语言而让我着迷的特性有很多,其中最喜欢的是接口设计,他让我们设计的东西具有美感。同样反射也是我比较喜欢的一个特性,他让程序 自动运行,动态加载成为了可能,同时也是现在很多流行框架所必不可少的特性,struts,hibernate等都是,spring本身就是基于反射的就 更不用说了。细细想来,似乎很少有不涉及到反射的框架。我自己设计框架的时候,开始也都是运用反射,但是越深入却让我越疑惑,反射的效率一直是我设计框架 的心病。
今天在优化
InstantMVC的 时候就考虑怎么提高自动封装form的效率,struts是用的commons-beantuils,好像也没人说struts的效率不高,诚 然,beanUtils中很多有用方便的特性让反射开发者着迷,但是通过我今天的测试,却发现beanUtils的易用性要付出巨大的性能代价,虽然在现 在这个年代,这么点性能不算什么,但是对于我这种执着的人开发执着的框架,还是对性能有种独特的偏好,目前来说InstantMVC中用的是直接的反射简 单封装,而InstantORM(我的持久层框架)中用到是自动生成pojo和相应的pojo辅助类来实现动态高效(比直接的反射高效10-20倍)执行 Object的方法(一般是get和set),对于InstantMVC的form利用动态生成辅助类有一定的难度,不是说实现难度,而是对于运用该框架 的web开发者来说,不够直接。所以还是主要考虑用反射的,废话不说,下面开始今天的测试。
首先,测试主要有三部分组成,测试创建对象的性能,测试set方法的性能,测试get方法的性能。我没有看过beanUtils的源代码,不过评我的经验 想想BeanUtils应该是做了一些性能的优化的,初步猜测是第一次运行缓存Object的相应东东(具体是什么也不知道),所以测试的时候都是从第二 次开始,忽略第一次。下面是测试代码(省略了异常抛出。)
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
public
class
MyBean{
Stringname;
int
age;
String[]firends;
public
static
void
main(Stringargs[])
{
Objecto1
=
beanUtilsCreate();
Objecto2
=
javaCreate();
MyBeanmy
=
new
MyBean();
long
a
=
System.currentTimeMillis();
for
(
int
i
=
0
;i
<
5000
;i
++
){
//
47
//
beanUtilsCreate();
//
15
//
javaCreate();
//
0
//
manualCreate();
//
235
//
beanUtilsSet(o1);
//
40
//
javaSet(o2);
//
0
//
manualSet(my);
//
203
//
beanUtilsGet(o1);
//
47
//
javaGet(o2);
//
0
//
manualGet(my);
}
long
b
=
System.currentTimeMillis();
System.out.println(b
-
a);
}
//
===============下面是beanUtils的方法
public
static
ObjectbeanUtilsCreate()
{
Objectob
=
ConstructorUtils.invokeConstructor(MyBean.
class
,
null
);
return
ob;
}
public
static
void
beanUtilsSet(Objectob)
{
BeanUtils.setProperty(ob,
"
name
"
,
"
旺旺旺
"
);
}
public
static
void
beanUtilsGet(Objectob)
{
BeanUtils.getProperty(ob,
"
name
"
);
}
//
===============下面是java自身的直接反射的方法
public
static
ObjectjavaCreate()
{
Objectob
=
MyBean.
class
.newInstance();
return
ob;
}
public
static
void
javaSet(Objectob)
{
Methodm
=
MyBean.
class
.getDeclaredMethod(
"
setName
"
,
new
Class[]{String.
class
});
m.invoke(ob,
new
Object[]{
"
旺旺旺
"
});
}
public
static
void
javaGet(Objectob)
{
Methodm
=
MyBean.
class
.getDeclaredMethod(
"
getName
"
,
new
Class[
0
]);
m.invoke(ob,
new
Object[
0
]);
}
//
===============下面是手动的创建对象
public
static
MyBeanmanualCreate(){
MyBeanmy
=
new
MyBean();
return
my;
}
public
static
void
manualSet(MyBeanmy){
my.setName(
"
旺旺旺
"
);
}
public
static
void
manualGet(MyBeanmy){
my.getName();
}
public
int
getAge(){
return
age;
}
public
void
setAge(
int
age){
this
.age
=
age;
}
public
String[]getFirends(){
return
firends;
}
public
void
setFirends(String[]firends){
this
.firends
=
firends;
}
public
StringgetName(){
return
name;
}
public
void
setName(Stringname){
this
.name
=
name;
}
}
上面代码首先创建一个MyBean,简单的name和age属性,然后get和set方法,在main方法中首先构建三个类:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
Objecto1
=
beanUtilsCreate();
Objecto2
=
javaCreate();
MyBeanmy
=
new
MyBean();
为了防止beanUtils内部对第一次做了缓存操作而使测试不准确。
第二次开始连续循环5000次分别测试 Create,set,和get的性能。
结果显示如下:
===================================================
BeanUtils java自己反射 手动
创建: 47 15 0
set方法 235 40 0
get方法 203 47 0
===================================================
jdk 1.6,1G内存,AMD 2600+
从上面的结果可以看出,BeanUtils的性能确实不怎么样,这样的结果虽然在现代服务器都菜价了的年代,我还是要为struts和spring等基于 反射的框架捏一把汗。不知道spring有没有对反射做过优化,不过上次看Ibatis的时候好像他提供了一个配置选项来增强字节码的反射效率,大概就是 那种动态创建字节码的技术吧。
#re: java反射性能测试分析[未登录] 2008-06-07 19:40 Ryan
反射生成对象,一般来说,一次生成多次使用,生成对象的时间对使用对象来说应该算是小的,但是如果你只生成一次,其他的什么都不做,那么本身也没什么意义,因为没有做任何动作回复更多评论
#re: java反射性能测试分析 2008-06-07 20:46 刹那
@Ryan
恩, 你说的有道理,一般生成一次都是缓存然后多次使用,但是对于有些应用却不竟然,比如struts的formBean,因为是在多线程情况下,所以不可能多 吃重复应用,再比如webwork的action,是非单利模式,也就是每次用户请求都要创建一个action,同样用到了反射。当然可以用clone技 术减少创建成本,但是不可否认,这种创建一次使用一次的情况还是有的。
再说,就算创建一次使用多次,但是每次调用set和get的开销还是比较大的。回复更多评论
#re: java反射性能测试分析 2008-06-08 04:17 YYX
对于Spring这种框架来说,一个bean生成了以后是长时间存在的。
而对于beanUtils这种简单应用5000次也就是1~2百毫秒时间,大部分时间只是执行一次,对于web应用,从底层数据一直执行至表现层,这点开销可以忽略不计。
执行效能优化以系统架构选择,储存策略,数据表数据库和SQL优化为主。
而且beanUtils也在节约开发时间同时也可以使代码更为通用。
另:不要把开源社区的人都当成神了,像beanUtils这种代码没什么可以优化的地方。回复更多评论
#re: java反射性能测试分析 2008-06-08 08:30 刹那
@YYX
这位老兄一定没做过webwork和spring整合开发。因为webwork的action是非单利的,用spring整合的时候需要每次一个请求反射一个action,而不是你说的都是一次生成长期使用(虽然在大多数情况下,spring还是最好弄单利)。
虽 然我知道整个开销对于web应用是可以忽略的,但是能优化,我还是要优化的,因为毕竟做的是框架类的东西,如果框架本身性能有问题,那么基于它上面的应用 就比较难说了。另外,beanUtils如果你作为一种tools,在平时开发的时候用用,那么没什么问题,但是如果集成到通用框架中,我不敢苟同,因为 本身通用框架中的组件被他人拿出来重用的可能性很小(保持框架内通用可重用即可),权衡这些,还是觉得beanUtils这种东西不适合做通用框架下集 成,但是却是和在普通的应用中使用。回复更多评论
#re: java反射性能测试分析 2008-06-16 12:27 newroc
每种JDK测试的效果会有些差别,我的结果是BeanUtils 要比自己 直接使用反射要快些的。回复更多评论
#re: java反射性能测试分析 2008-06-16 16:06 刹那
@newroc
你的是jdk几?我是1.6回复更多评论
#re: java反射性能测试分析 2008-06-17 15:54 z-bro
仔细阅读了这段代码,并且阅读了beanutils-1.8.0 ConstructorUtils与setProperty代码,很遗憾我没有发现有优化执行效率的迹象存在.在ConstructorUtils部分程 序结构相对简单,代码冗余相对较小;而setProperty部分,有相当部分log代码(可以通过开关关闭),并且有大量的细致工作代 码.beanutils中在ConstructorUtils与setProperty部分也没有缓存的机制存在.另外补充一句:刹那 的这份测试代码都使用了java缺省的类加载器,这3种加载(创建)方式(BeanUtils,java自己反射,手动)在第一次之后,都是使用了 ClassLoader的缓存,而并没有重新读类文件.
结合 刹那 的分析报告,我个人认为beanutils不能作为强调高效框架的首选,因为"如果你只生成一次,其他的什么都不做,那么本身也没什么意义",但是如果如YXX考虑的那样,可能是个不错的选择
回复更多评论
#re: java反射性能测试分析 2008-06-25 16:37 YYX
@刹那
spring 和struts2的整合项目 我正在做。
你那个beanutils运行一次要把bean里面的所有getter和setter遍历一次,当比你其他的地方专门指定某方法执行的慢多了,要比也要所有属性get,set一次再和beanutils比。
另外我说的这个性能差距可以忽略,的确是这样的,从数据库以后的逻辑运算比起数据库查询需要的时间,本来就是可以忽略的。
就好比一个亿万富翁,不会一大早出远门到便宜的地方去买菜。哪怕楼下的菜贵10倍,只要东西一样,就行。回复更多评论
#re: java反射性能测试分析 2008-06-25 16:50 YYX
真正要提高性能,不是在这种小地方下手,而是整体架构的设计,查询语句,数据库的配置,缓存的配置,运算量的分布。这些方面改善一点,比5000次copyProperties()节约时间还长。
回复更多评论
#re: java反射性能测试分析[未登录] 2008-09-09 22:53 P
看到beanUtil里面用set/getProperty()了吗?人家为什么这么做?你那个类有通用性吗?回复更多评论