论坛首页 Java企业应用论坛

cglib相关性能测试对比

浏览 9110 次
精华帖 (1) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-11-03  

背景: 

 

继上一篇文章 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;
......// 一堆的setter/getter方法
}

 

 说明: 该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(); // 进行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() {
        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数据

 

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)

 

  • // 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)
  • // 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);
3. BeanUtils (apache-common)
  • // 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);

测试结果: 

 

测试次数: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 
// 测试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

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

最后

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

 

 

   发表时间:2010-11-05  
LZ用的应该是SUN的JVM吧.建议测试下JRockit下的性能.
0 请登录后投票
   发表时间:2010-11-05  
J9的性能如何?
0 请登录后投票
   发表时间:2010-11-09  
ouchxp 写道
LZ用的应该是SUN的JVM吧.建议测试下JRockit下的性能.


恩,用的是jdk 1.6.18,下次可以我装一下JRockit, openjdk测试做一个比较。

针对cglib beanCopier和BeanUtils的性能查了接近860多倍,有点让我诧异

原先在自己笔记本双核2G内存跑了,只查了10倍左右,没想到上了服务器,配置了一下jvm参数,性能差距就这么明显

看来以后做测试还得尽量找一下服务器跑跑,
0 请登录后投票
   发表时间:2011-03-17  
看不懂哇……
0 请登录后投票
   发表时间:2011-03-17  
ganjp 写道
看不懂哇……

http://rednaxelafx.iteye.com/blog/548536
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics