cglib相关性能测试对比

简介:

背景: 

 

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

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

 

正题:

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

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接口。


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

 定义测试的模板方法 

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


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() {

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数据

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 参数 

  • 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)

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) 

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)

  • 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 

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


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

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

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

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

最后

测试数据仅拱参考,最后测试代码可见附件。测试方法如存在问题,欢迎拍砖
相关文章
|
5天前
|
人工智能 API 内存技术
刚刚 DeepSeek V4.1 Flash 开启内测,1 分钟教你用上!
刚刚 DeepSeek 内测群发布了 DeepSeek V4.1 Flash 中间版本内测的消息,这次的模型采用了新的结构,原生支持多模态、能力更强、速度更快、且成本更低。
1702 7
|
9天前
|
人工智能 运维 BI
阿里云千问办公QwenWork深度解析:基于Qwen3.8,六大核心能力重构企业全自动化工作流与计费选型指南
传统AI办公工具大多停留在对话问答、文档摘要、简单文案生成层面,只能完成单点碎片化任务,无法自主拆解复杂业务流程,很难串联多工具、多文档、外部业务系统完成端到端完整工作交付。很多企业在落地AI办公的时候,需要组合多款不同工具,来回切换界面,手动复制粘贴中间结果,智能化改造落地门槛居高不下。千问办公QwenWork是整合多款智能体产品能力打造的一体化企业办公智能体平台,底层基座依托Qwen3.8大模型,打通桌面端Agent、云端Agent、企业协同Agent三种运行形态,不再局限简单问答,接收业务目标之后自主拆解任务步骤,调用各类工具,处理文档、表格、浏览器自动化、数据查询,直接输出可交付的办公
1627 2
|
6天前
|
SQL 人工智能 前端开发
QoderWake 1.0 正式发布:从桌面里的 Agent,到工作现场的数字员工
QoderWake v1.0正式发布:企业级数字员工团队平台。支持“一句话建岗”,预置10类特训岗位;Waker常驻钉钉/飞书群,@即响应、自动协作、跨任务记忆;具备定时/事件/API多触发方式与统一任务看板;已沉淀27.6万条记忆、12.3万项技能,助力组织实现人机协同增效。
745 1
|
10天前
|
网络协议 Linux iOS开发
【2026实测】Wireshark下载+安装+汉化+使用教程(图文版,巨详细)
Wireshark 是一款免费开源的网络协议分析工具,可实时捕获、解析并可视化数据包,助你诊断网络故障、分析通信协议(如HTTP、DNS、TCP等)。支持Windows/macOS/Linux,含中文界面,新手入门便捷。(239字)
|
18天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
3903 5
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
4天前
|
缓存 测试技术 API
DeepSeek V4.1 Flash 内测接入:改个模型名即可调用(附代码)
DeepSeek V4.1 Flash 内测不用申请,base_url 不变、改个模型名就能调,9/10 到期。本文讲清接入、计费限流与多模态注意点。
726 0
DeepSeek V4.1 Flash 内测接入:改个模型名即可调用(附代码)
|
9天前
|
人工智能 自然语言处理 安全
阿里云AI数智鉴密:AI 生成内容如何拿到一张"防篡改的身份证"
隐形水印 + C2PA签名:让AI生成内容“持证上岗”。
1148 0
|
11天前
|
缓存 数据可视化 开发工具
DeepSeek Harness 怎么更新?dsh 更新完整指南:更新本体(npx、npm、源码)与更新插件两种方式
DeepSeek Harness 的更新分两层:本体更新(npx 自动最新、npm update -g、源码 git pull)与插件更新(插件市场点更新、命令行覆盖安装)。本文按「准备 → 更新本体 → 更新插件 → 更新后检查」四步走,覆盖新手常见疑问。
1330 1
DeepSeek Harness 怎么更新?dsh 更新完整指南:更新本体(npx、npm、源码)与更新插件两种方式

热门文章

最新文章