您的位置:首页 > 其它

cglib相关性能测试对比

2011-05-11 10:41 525 查看
背景:

继上一篇文章 cglib源码学习交流

很多同学提出,因中文文档缺乏,导致对文章中的介绍看的不是很明白,更多的只是想了解具体的使用即可。所以趁势写了这篇博文,主要是将cglib中的几个工具类和常用的Reflect ,BeanUtils做一个对比,顺便也介绍一下cglib的相关用法,一举两得,望大家多多支持。

正题:

1. 首先定义一份Pojo Bean ,后续的测试主要围绕这个进行。

Java代码



public

static

class
CopyBean {

private

int
intValue;

private

boolean
boolValue;

private

float
floatValue;

private

double
doubleValue;

private

long
longValue;

private

char
charValue;

private

byte
byteValue;

private

short
shortValue;

private
Integer integerValue;

private
Boolean boolObjValue;

private
Float floatObjValue;

private
Double doubleObjValue;

private
Long longObjValue;

private
Short shortObjValue;

private
Byte byteObjValue;

private
BigInteger bigIntegerValue;

private
BigDecimal bigDecimalValue;

private
String stringValue;

......// 一堆的setter/getter方法

}

说明: 该copyBean基本包含了java的所有原型对象,基本对象,和常用的BigDecimal,BigInteger,总共17个属性。

2. 定义测试模板 (模板模式)

定义一个TestCallback接口。

Java代码



interface
TestCallback {

String getName();

CglibPerformanceTest.CopyBean call(CglibPerformanceTest.CopyBean source);

}

定义测试的模板方法
private static final DecimalFormat integerFormat = new DecimalFormat("#,###");

Java代码



public

static

void
testTemplate(TestCallback callback, CopyBean source,
int
count) {

int
warmup =
10
;

// 先进行预热,加载一些类,避免影响测试

for
(
int
i =
0
; i < warmup; i++) {

callback.call(source);

}

restoreJvm(); // 进行GC回收

// 进行测试

long
start = System.nanoTime();

for
(
int
i =
0
; i < count; i++) {

callback.call(source);

}

long
nscost = (System.nanoTime() - start);

System.out.println(callback.getName() + " total cost="
+ integerFormat.format(nscost) +
"ns , each cost="

+ nscost / count + "ns"
);

restoreJvm();// 进行GC回收

}

说明:

为了测试更加精确,避免因为在一次的循环中进行处理,jvm内存,GC,Class装载对测试的影响,有一个warmup的过程,先执行少量的测试方法,这里是执行10次

避免jvm内存GC对测试id影响,这里有restoreJvm强制进行一次jvm GC

restoreJvm相关方法:

private static void restoreJvm() {

Java代码



int
maxRestoreJvmLoops =
10
;

long
memUsedPrev = memoryUsed();

for
(
int
i =
0
; i < maxRestoreJvmLoops; i++) {

System.runFinalization();

System.gc();

long
memUsedNow = memoryUsed();

// 如果多次GC后内存稳定了,就退出

if
((ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount() ==
0
)

&& (memUsedNow >= memUsedPrev)) {

break
;

} else
{

memUsedPrev = memUsedNow;

}

}

}

private

static

long
memoryUsed() {

Runtime rt = Runtime.getRuntime();

return
rt.totalMemory() - rt.freeMemory();

}

3. 准备原始的CopyBean数据

Java代码



private

static
CopyBean getBean() {

CopyBean bean = new
CopyBean();

bean.setIntValue(1
);

bean.setBoolValue(false
);

bean.setFloatValue(1
.0f);

bean.setDoubleValue(1
.0d);

bean.setLongValue(1l);

bean.setCharValue('a'
);

bean.setShortValue((short
)
1
);

bean.setByteValue((byte
)
1
);

bean.setIntegerValue(new
Integer(
"1"
));

bean.setBoolObjValue(new
Boolean(
"false"
));

bean.setFloatObjValue(new
Float(
"1.0"
));

bean.setDoubleObjValue(new
Double(
"1.0"
));

bean.setLongObjValue(new
Long(
"1"
));

bean.setShortObjValue(new
Short(
"1"
));

bean.setByteObjValue(new
Byte(
"1"
));

bean.setBigIntegerValue(new
BigInteger(
"1"
));

bean.setBigDecimalValue(new
BigDecimal(
"1"
));

bean.setStringValue("1"
);

return
bean;

}

4. 执行相关测试

测试环境说明:

操作系统 Linux ccbu-156-49 2.6.18-131.el5.customxen #1 SMP Tue Sep 15 15:46:11 CST 2009 x86_64 x86_64 x86_64 GNU/Linux

虚拟8cpu , 5G内存

jdk 1.6.0_18

jvm 参数

Jvm参数代码



-server -Xmx2g -Xms2g -Xmn512m -XX:PermSize=196m -Xss256k -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=
70

第一测试主要是一个对象的全部属性
进行拷贝

BeanCopier (cglib)

PropertyUtils (apache-common)

BeanUtils (apache-common)

1. BeanCopier (cglib)

Java代码



// beanCopier测试

final
BeanCopier beanCopier = BeanCopier.create(CopyBean.
class
, CopyBean.
class
,
false
);

final
CopyBean beanCopierTarget =
new
CopyBean();
//new一次,避免new对象产生的代价影响测试结果

testTemplate(new
TestCallback() {

public
String getName() {

return

"BeanCopier"
;

}

public
CopyBean call(CopyBean source) {

beanCopier.copy(source, beanCopierTarget, null
);

return
beanCopierTarget;

}

}, bean, testCount);

2. PropertyUtils (apache-common)

Java代码



<span style=
"background-color: rgba(0, 0, 0, 0);"
>
// PropertyUtils测试

final
CopyBean propertyUtilsTarget =
new
CopyBean();

testTemplate(new
TestCallback() {

public
String getName() {

return

"PropertyUtils"
;

}

public
CopyBean call(CopyBean source) {

try
{

PropertyUtils.copyProperties(propertyUtilsTarget, source);

} catch
(Exception e) {

e.printStackTrace();

}

return
propertyUtilsTarget;

}

}, bean, testCount);</span>

3. BeanUtils (apache-common)

Java代码



<span style=
"background-color: rgba(0, 0, 0, 0);"
>
// BeanUtils测试

final
CopyBean beanUtilsTarget =
new
CopyBean();

testTemplate(new
TestCallback() {

public
String getName() {

return

"BeanUtils"
;

}

public
CopyBean call(CopyBean source) {

try
{

BeanUtils.copyProperties(beanUtilsTarget, source);

} catch
(Exception e) {

e.printStackTrace();

}

return
beanUtilsTarget;

}

}, bean, testCount);</span>

测试结果:

测试次数:testCount = 1000 * 1000 = 100万次

BeanCopier total cost=36,626,000ns , each cost=36ns

PropertyUtils total cost=18,173,767,000ns , each cost=18173ns

BeanUtils total cost=31,236,079,000ns , each cost=31236ns

从这个结果可以看出, BeanCopier是PropertyUtils的504倍, PropertyUtils是BeanUtils的1.71倍, BeanCopier是PropertyUtils的861.84倍,差了近3个数量级。

第二测试主要是一个对象的单个属性
进行拷贝

BulkBean (cglib)

BeanMap (cglib)

FastClass/FastMethod (cglib)

未处理的jdk reflect (jdk)

处理的jdk reflect (jdk)

1. BulkBean

Java代码



// 测试BulkBean

final
BulkBean bulkBean = BulkBean.create(bean.getClass(),
new
String[] { getMethodName },

new
String[] { setMethodName },
new
Class[] { Integer.
class
});

final
CopyBean bulkBeanTarget =
new
CopyBean();

testTemplate(new
TestCallback() {

@Override

public
String getName() {

return

"BulkBean"
;

}

@Override

public
CopyBean call(CopyBean source) {

Object[] result = bulkBean.getPropertyValues(source); // 先调用getter

bulkBean.setPropertyValues(bulkBeanTarget, result); // 再调用setter

return
bulkBeanTarget;

}

}, bean, testCount);

2. BeanMap

Java代码



// 测试BeanMap

final
BeanMap sourceMap = BeanMap.create(bean);
// 预先创建对象

final
BeanMap targetMap = BeanMap.create(
new
CopyBean());

final
CopyBean beanMapTarget =
new
CopyBean();

testTemplate(new
TestCallback() {

@Override

public
String getName() {

return

"BeanMap"
;

}

@Override

public
CopyBean call(CopyBean source) {

targetMap.setBean(beanMapTarget); // 将目标对象设置于beanMap

Object obj = sourceMap.get(fieldName);

targetMap.put(fieldName, obj);

return
beanMapTarget;

}

}, bean, testCount);

3. FastClass/FastMethod

Java代码



// 测试FastClass

final
FastClass fastClass = FastClass.create(bean.getClass());

final
FastMethod setFastMetod = fastClass.getMethod(setMethodName,
new
Class[] { Integer.
class
});

final
FastMethod getFastMetod = fastClass.getMethod(getMethodName,
new
Class[] {});

final
CopyBean fastClassTarget =
new
CopyBean();

testTemplate(new
TestCallback() {

@Override

public
String getName() {

return

"FastClass"
;

}

@Override

public
CopyBean call(CopyBean source) {

try
{

Object field = getFastMetod.invoke(source, new
Object[] {});
// 调用get方法

setFastMetod.invoke(fastClassTarget, new
Object[] { field });
// 调用set方法赋值

} catch
(Exception e) {

e.printStackTrace();

}

return
fastClassTarget;

}

}, bean, testCount);

4. 未处理的jdk reflect

Java代码



try
{

// 进行method对象cache,真实应用中一般都会cache method对象

final
Method getMethod = bean.getClass().getMethod(getMethodName,
new
Class[] {});

final
Method setMethod = bean.getClass().getMethod(setMethodName,
new
Class[] { Integer.
class
});

// 测试未优化过的Reflect

final
CopyBean reflect1Target =
new
CopyBean();

testTemplate(new
TestCallback() {

@Override

public
String getName() {

return

"未优化过的Reflect"
;

}

@Override

public
CopyBean call(CopyBean source) {

try
{

Object field = getMethod.invoke(source, new
Object[] {});

setMethod.invoke(reflect1Target, new
Object[] { field });

} catch
(Exception e) {

e.printStackTrace();

}

return
reflect1Target;

}

}, bean, testCount);

} catch
(Exception e1) {

e1.printStackTrace();

}

}

5. 处理过的jdk reflect

Java代码



try
{

// 进行method对象cache,真实应用中一般都会cache method对象

final
Method getMethod = bean.getClass().getMethod(getMethodName,
new
Class[] {});

final
Method setMethod = bean.getClass().getMethod(setMethodName,
new
Class[] { Integer.
class
});

// 测试优化过的Reflect

getMethod.setAccessible(true
);
// 设置不进行access权限检查

setMethod.setAccessible(true
);
// 设置不进行access权限检查

final
CopyBean reflect2Target =
new
CopyBean();

testTemplate(new
TestCallback() {

@Override

public
String getName() {

return

"优化过的Reflect"
;

}

@Override

public
CopyBean call(CopyBean source) {

try
{

Object field = getMethod.invoke(source, new
Object[] {});

setMethod.invoke(reflect2Target, new
Object[] { field });

} catch
(Exception e) {

e.printStackTrace();

}

return
reflect2Target;

}

}, bean, testCount);

} catch
(Exception e1) {

e1.printStackTrace();

}

测试结果:

测试次数:testCount = 1000 * 1000 * 100 = 1亿次

BulkBean total cost=2,125,759,000ns , each cost=21ns

BeanMap total cost=2,730,912,000ns , each cost=27ns

FastClass total cost=2,576,470,000ns , each cost=25ns

未处理过的Reflect total cost=2,882,755,000ns , each cost=28ns

处理过的Reflect total cost=2,792,828,000ns , each cost=27ns

测试结果,性能相差不多,差距不大,这也可以说明jdk对reflect调用的优化已经做的很棒了。

最后

测试数据仅拱参考,最后测试代码可见附件。测试方法如存在问题,欢迎拍砖
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: