背景:
继上一篇文章 cglib源码学习交流
很多同学提出,因中文文档缺乏,导致对文章中的介绍看的不是很明白,更多的只是想了解具体的使用即可。所以趁势写了这篇博文,主要是将cglib中的几个工具类和常用的Reflect ,BeanUtils做一个对比,顺便也介绍一下cglib的相关用法,一举两得,望大家多多支持。
正题:
1. 首先定义一份Pojo Bean ,后续的测试主要围绕这个进行。
- 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;
- ......
- }
说明: 该copyBean基本包含了java的所有原型对象,基本对象,和常用的BigDecimal,BigInteger,总共17个属性。
2. 定义测试模板 (模板模式)
定义一个TestCallback接口。
- interface TestCallback {
-
- String getName();
-
- CglibPerformanceTest.CopyBean call(CglibPerformanceTest.CopyBean source);
- }
定义测试的模板方法
private static final DecimalFormat integerFormat = new DecimalFormat("#,###");
- public static void testTemplate(TestCallback callback, CopyBean source, int count) {
- int warmup = 10 ;
-
- for ( int i = 0 ; i < warmup; i++) {
- callback.call(source);
- }
- restoreJvm();
-
- 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();
-
- }
说明:
- 为了测试更加精确,避免因为在一次的循环中进行处理,jvm内存,GC,Class装载对测试的影响,有一个warmup的过程,先执行少量的测试方法,这里是执行10次
- 避免jvm内存GC对测试id影响,这里有restoreJvm强制进行一次jvm GC
restoreJvm相关方法:
private static void restoreJvm() {
- int maxRestoreJvmLoops = 10 ;
- long memUsedPrev = memoryUsed();
- for ( int i = 0 ; i < maxRestoreJvmLoops; i++) {
- System.runFinalization();
- System.gc();
-
- long memUsedNow = memoryUsed();
-
- 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数据
- 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 参数
-
- -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)
-
-
- final BeanCopier beanCopier = BeanCopier.create(CopyBean. class , CopyBean. class , false );
- final CopyBean beanCopierTarget = new CopyBean();
- 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)
-
- <span style= "background-color: rgba(0, 0, 0, 0);" >
- 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)
-
- <span style= "background-color: rgba(0, 0, 0, 0);" >
- 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
-
- 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);
- bulkBean.setPropertyValues(bulkBeanTarget, result);
- return bulkBeanTarget;
- }
-
- }, bean, testCount);
2. 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);
- Object obj = sourceMap.get(fieldName);
- targetMap.put(fieldName, obj);
- return beanMapTarget;
- }
-
- }, bean, testCount);
3. FastClass/FastMethod
-
- 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[] {});
- setFastMetod.invoke(fastClassTarget, new Object[] { field });
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- return fastClassTarget;
- }
-
- }, bean, testCount);
4. 未处理的jdk reflect
- try {
-
- final Method getMethod = bean.getClass().getMethod(getMethodName, new Class[] {});
- final Method setMethod = bean.getClass().getMethod(setMethodName, new Class[] { Integer. class });
-
- 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
- try {
-
- final Method getMethod = bean.getClass().getMethod(getMethodName, new Class[] {});
- final Method setMethod = bean.getClass().getMethod(setMethodName, new Class[] { Integer. class });
-
- getMethod.setAccessible(true );
- setMethod.setAccessible(true );
- 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调用的优化已经做的很棒了。
最后
测试数据仅拱参考,最后测试代码可见附件。测试方法如存在问题,欢迎拍砖