背景:
继上一篇文章 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调用的优化已经做的很棒了。
最后
测试数据仅拱参考,最后测试代码可见附件。测试方法如存在问题,欢迎拍砖