cglib相关性能测试对比

背景: 

 

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

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

 

正题:

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

 

 

Java代码     收藏代码
  1. public   static   class  CopyBean {  
  2.   
  3.         private   int         intValue;  
  4.         private   boolean     boolValue;  
  5.         private   float       floatValue;  
  6.         private   double      doubleValue;  
  7.         private   long        longValue;  
  8.         private   char        charValue;  
  9.         private   byte        byteValue;  
  10.         private   short       shortValue;  
  11.         private  Integer    integerValue;  
  12.         private  Boolean    boolObjValue;  
  13.         private  Float      floatObjValue;  
  14.         private  Double     doubleObjValue;  
  15.         private  Long       longObjValue;  
  16.         private  Short      shortObjValue;  
  17.         private  Byte       byteObjValue;  
  18.         private  BigInteger bigIntegerValue;  
  19.         private  BigDecimal bigDecimalValue;  
  20.         private  String     stringValue;  
  21. ......// 一堆的setter/getter方法   
  22. }  

 

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

 

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

 

   定义一个TestCallback接口。

 

Java代码     收藏代码
  1. interface  TestCallback {  
  2.   
  3.     String getName();  
  4.   
  5.     CglibPerformanceTest.CopyBean call(CglibPerformanceTest.CopyBean source);  
  6. }  

 

 

   定义测试的模板方法 

   private static final DecimalFormat integerFormat = new DecimalFormat("#,###");

Java代码     收藏代码
  1. public   static   void  testTemplate(TestCallback callback, CopyBean source,  int  count) {  
  2.         int  warmup =  10 ;  
  3.         // 先进行预热,加载一些类,避免影响测试   
  4.         for  ( int  i =  0 ; i < warmup; i++) {  
  5.             callback.call(source);  
  6.         }  
  7.         restoreJvm(); // 进行GC回收   
  8.         // 进行测试   
  9.         long  start = System.nanoTime();  
  10.         for  ( int  i =  0 ; i < count; i++) {  
  11.             callback.call(source);  
  12.         }  
  13.         long  nscost = (System.nanoTime() - start);  
  14.         System.out.println(callback.getName() + " total cost="  + integerFormat.format(nscost) +  "ns , each cost="   
  15.                            + nscost / count  + "ns" );  
  16.         restoreJvm();// 进行GC回收   
  17.   
  18.     }  

 

 

 说明:

 

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

   restoreJvm相关方法:

    private static void restoreJvm() {
Java代码     收藏代码
  1.      int  maxRestoreJvmLoops =  10 ;  
  2.     long  memUsedPrev = memoryUsed();  
  3.     for  ( int  i =  0 ; i < maxRestoreJvmLoops; i++) {  
  4.         System.runFinalization();  
  5.         System.gc();  
  6.   
  7.         long  memUsedNow = memoryUsed();  
  8.         //  如果多次GC后内存稳定了,就退出    
  9.         if  ((ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount() ==  0 )  
  10.             && (memUsedNow >= memUsedPrev)) {  
  11.             break ;  
  12.         } else  {  
  13.             memUsedPrev = memUsedNow;  
  14.         }  
  15.     }  
  16. }  
  17.   
  18. private   static   long  memoryUsed() {  
  19.     Runtime rt = Runtime.getRuntime();  
  20.     return  rt.totalMemory() - rt.freeMemory();  
  21. }  

 

3.  准备原始的CopyBean数据

 

Java代码     收藏代码
  1. private   static  CopyBean getBean() {  
  2.         CopyBean bean = new  CopyBean();  
  3.         bean.setIntValue(1 );  
  4.         bean.setBoolValue(false );  
  5.         bean.setFloatValue(1 .0f);  
  6.         bean.setDoubleValue(1 .0d);  
  7.         bean.setLongValue(1l);  
  8.         bean.setCharValue('a' );  
  9.         bean.setShortValue((short 1 );  
  10.         bean.setByteValue((byte 1 );  
  11.         bean.setIntegerValue(new  Integer( "1" ));  
  12.         bean.setBoolObjValue(new  Boolean( "false" ));  
  13.         bean.setFloatObjValue(new  Float( "1.0" ));  
  14.         bean.setDoubleObjValue(new  Double( "1.0" ));  
  15.         bean.setLongObjValue(new  Long( "1" ));  
  16.         bean.setShortObjValue(new  Short( "1" ));  
  17.         bean.setByteObjValue(new  Byte( "1" ));  
  18.         bean.setBigIntegerValue(new  BigInteger( "1" ));  
  19.         bean.setBigDecimalValue(new  BigDecimal( "1" ));  
  20.         bean.setStringValue("1" );  
  21.         return  bean;  
  22.     }  

 

 

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参数代码     收藏代码
    1. -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代码     收藏代码
    1. // beanCopier测试   
    2.      final  BeanCopier beanCopier = BeanCopier.create(CopyBean. class , CopyBean. class false );  
    3.         final  CopyBean beanCopierTarget =  new  CopyBean(); //new一次,避免new对象产生的代价影响测试结果   
    4.         testTemplate(new  TestCallback() {  
    5.   
    6.             public  String getName() {  
    7.                 return   "BeanCopier" ;  
    8.             }  
    9.   
    10.             public  CopyBean call(CopyBean source) {  
    11.                 beanCopier.copy(source, beanCopierTarget, null );  
    12.                 return  beanCopierTarget;  
    13.             }  
    14.         }, bean, testCount);  
2.  PropertyUtils (apache-common)
  • Java代码     收藏代码
    1. <span style= "background-color: rgba(0, 0, 0, 0);" > // PropertyUtils测试   
    2.         final  CopyBean propertyUtilsTarget =  new  CopyBean();  
    3.         testTemplate(new  TestCallback() {  
    4.   
    5.             public  String getName() {  
    6.                 return   "PropertyUtils" ;  
    7.             }  
    8.   
    9.             public  CopyBean call(CopyBean source) {  
    10.                 try  {  
    11.                     PropertyUtils.copyProperties(propertyUtilsTarget, source);  
    12.                 } catch  (Exception e) {  
    13.                     e.printStackTrace();  
    14.                 }  
    15.                 return  propertyUtilsTarget;  
    16.             }  
    17.   
    18.         }, bean, testCount);</span>  
3. BeanUtils (apache-common)
  • Java代码     收藏代码
    1. <span style= "background-color: rgba(0, 0, 0, 0);" > // BeanUtils测试   
    2.         final  CopyBean beanUtilsTarget =  new  CopyBean();  
    3.         testTemplate(new  TestCallback() {  
    4.   
    5.             public  String getName() {  
    6.                 return   "BeanUtils" ;  
    7.             }  
    8.   
    9.             public  CopyBean call(CopyBean source) {  
    10.                 try  {  
    11.                     BeanUtils.copyProperties(beanUtilsTarget, source);  
    12.                 } catch  (Exception e) {  
    13.                     e.printStackTrace();  
    14.                 }  
    15.                 return  beanUtilsTarget;  
    16.             }  
    17.   
    18.         }, 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代码     收藏代码
  1. // 测试BulkBean   
  2.         final  BulkBean bulkBean = BulkBean.create(bean.getClass(),  new  String[] { getMethodName },  
  3.                                                   new  String[] { setMethodName },  new  Class[] { Integer. class  });  
  4.         final  CopyBean bulkBeanTarget =  new  CopyBean();  
  5.         testTemplate(new  TestCallback() {  
  6.   
  7.             @Override   
  8.             public  String getName() {  
  9.                 return   "BulkBean" ;  
  10.             }  
  11.   
  12.             @Override   
  13.             public  CopyBean call(CopyBean source) {  
  14.                 Object[] result = bulkBean.getPropertyValues(source); // 先调用getter   
  15.                 bulkBean.setPropertyValues(bulkBeanTarget, result); // 再调用setter   
  16.                 return  bulkBeanTarget;  
  17.             }  
  18.   
  19.         }, bean, testCount);  
 
2. BeanMap

Java代码     收藏代码
  1. // 测试BeanMap   
  2.         final  BeanMap sourceMap = BeanMap.create(bean);  // 预先创建对象   
  3.         final  BeanMap targetMap = BeanMap.create( new  CopyBean());  
  4.         final  CopyBean beanMapTarget =  new  CopyBean();  
  5.         testTemplate(new  TestCallback() {  
  6.   
  7.             @Override   
  8.             public  String getName() {  
  9.                 return   "BeanMap" ;  
  10.             }  
  11.   
  12.             @Override   
  13.             public  CopyBean call(CopyBean source) {  
  14.                 targetMap.setBean(beanMapTarget); // 将目标对象设置于beanMap   
  15.                 Object obj = sourceMap.get(fieldName);  
  16.                 targetMap.put(fieldName, obj);  
  17.                 return  beanMapTarget;  
  18.             }  
  19.   
  20.         }, bean, testCount);  

 3. FastClass/FastMethod
Java代码     收藏代码
  1. // 测试FastClass   
  2.         final  FastClass fastClass = FastClass.create(bean.getClass());  
  3.         final  FastMethod setFastMetod = fastClass.getMethod(setMethodName,  new  Class[] { Integer. class  });  
  4.         final  FastMethod getFastMetod = fastClass.getMethod(getMethodName,  new  Class[] {});  
  5.         final  CopyBean fastClassTarget =  new  CopyBean();  
  6.         testTemplate(new  TestCallback() {  
  7.   
  8.             @Override   
  9.             public  String getName() {  
  10.                 return   "FastClass" ;  
  11.             }  
  12.   
  13.             @Override   
  14.             public  CopyBean call(CopyBean source) {  
  15.   
  16.                 try  {  
  17.                     Object field = getFastMetod.invoke(source, new  Object[] {}); // 调用get方法   
  18.                     setFastMetod.invoke(fastClassTarget, new  Object[] { field }); // 调用set方法赋值   
  19.                 } catch  (Exception e) {  
  20.                     e.printStackTrace();  
  21.                 }  
  22.   
  23.                 return  fastClassTarget;  
  24.             }  
  25.   
  26.         }, bean, testCount);  
 
4.  未处理的jdk reflect
Java代码     收藏代码
  1. try  {  
  2.             // 进行method对象cache,真实应用中一般都会cache method对象   
  3.             final  Method getMethod = bean.getClass().getMethod(getMethodName,  new  Class[] {});  
  4.             final  Method setMethod = bean.getClass().getMethod(setMethodName,  new  Class[] { Integer. class  });  
  5.             // 测试未优化过的Reflect   
  6.             final  CopyBean reflect1Target =  new  CopyBean();  
  7.             testTemplate(new  TestCallback() {  
  8.   
  9.                 @Override   
  10.                 public  String getName() {  
  11.                     return   "未优化过的Reflect" ;  
  12.                 }  
  13.   
  14.                 @Override   
  15.                 public  CopyBean call(CopyBean source) {  
  16.                     try  {  
  17.                         Object field = getMethod.invoke(source, new  Object[] {});  
  18.                         setMethod.invoke(reflect1Target, new  Object[] { field });  
  19.                     } catch  (Exception e) {  
  20.                         e.printStackTrace();  
  21.                     }  
  22.                     return  reflect1Target;  
  23.                 }  
  24.   
  25.             }, bean, testCount);  
  26.   
  27.         } catch  (Exception e1) {  
  28.             e1.printStackTrace();  
  29.         }  
  30.   
  31.     }  
 

5. 处理过的jdk reflect
Java代码     收藏代码
  1. try  {  
  2.             // 进行method对象cache,真实应用中一般都会cache method对象   
  3.             final  Method getMethod = bean.getClass().getMethod(getMethodName,  new  Class[] {});  
  4.             final  Method setMethod = bean.getClass().getMethod(setMethodName,  new  Class[] { Integer. class  });  
  5.             // 测试优化过的Reflect   
  6.             getMethod.setAccessible(true ); // 设置不进行access权限检查   
  7.             setMethod.setAccessible(true ); // 设置不进行access权限检查   
  8.             final  CopyBean reflect2Target =  new  CopyBean();  
  9.             testTemplate(new  TestCallback() {  
  10.   
  11.                 @Override   
  12.                 public  String getName() {  
  13.                     return   "优化过的Reflect" ;  
  14.                 }  
  15.   
  16.                 @Override   
  17.                 public  CopyBean call(CopyBean source) {  
  18.                     try  {  
  19.                         Object field = getMethod.invoke(source, new  Object[] {});  
  20.                         setMethod.invoke(reflect2Target, new  Object[] { field });  
  21.                     } catch  (Exception e) {  
  22.                         e.printStackTrace();  
  23.                     }  
  24.                     return  reflect2Target;  
  25.                 }  
  26.   
  27.             }, bean, testCount);  
  28.         } catch  (Exception e1) {  
  29.             e1.printStackTrace();  
  30.         }  
 

 

 

测试结果:  

 

测试次数: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调用的优化已经做的很棒了。

最后

测试数据仅拱参考,最后测试代码可见附件。测试方法如存在问题,欢迎拍砖

你可能感兴趣的:(java,jvm,jdk,bean,测试,each)