http://www.cnblogs.com/haiq/p/4304615.html
都说 Cglib 创建的动态代理的运行性能比 JDK 动态代理能高出大概 10 倍,今日抱着怀疑精神验证了一下,发现情况有所不同,遂贴出实验结果,以供参考和讨论。
代码很简单,首先,定义一个 Test 接口,和一个实现 TestImpl 。Test 接口仅定义一个方法 test,对传入的 int 参数加 1 后返回。代码如下:
package my.test; public interface Test { public int test(int i); }
package my.test; public class TestImpl implements Test{ public int test(int i) { return i+1; } }
然后,定义了三种代理的实现:装饰者模式实现的代理(decorator),JDK 动态代理(dynamic proxy) 和 Cglib 动态代理 (cglib proxy)。代码如下:
package my.test; public class DecoratorTest implements Test{ private Test target; public DecoratorTest(Test target) { this.target = target; } public int test(int i) { return target.test(i); } }
package my.test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class DynamicProxyTest implements InvocationHandler { private Test target; private DynamicProxyTest(Test target) { this.target = target; } public static Test newProxyInstance(Test target) { return (Test) Proxy .newProxyInstance(DynamicProxyTest.class.getClassLoader(), new Class<?>[] { Test.class }, new DynamicProxyTest(target)); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(target, args); } }
package my.test; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxyTest implements MethodInterceptor { private CglibProxyTest() { } public static <T extends Test> Test newProxyInstance(Class<T> targetInstanceClazz){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetInstanceClazz); enhancer.setCallback(new CglibProxyTest()); return (Test) enhancer.create(); } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } }
以 TestImpl 的调用耗时作为基准,对比通过其它三种代理进行调用的耗时。测试代码如下:
package my.test; import java.util.LinkedHashMap; import java.util.Map; public class ProxyPerfTester { public static void main(String[] args) { //创建测试对象; Test nativeTest = new TestImpl(); Test decorator = new DecoratorTest(nativeTest); Test dynamicProxy = DynamicProxyTest.newProxyInstance(nativeTest); Test cglibProxy = CglibProxyTest.newProxyInstance(TestImpl.class); //预热一下; int preRunCount = 10000; runWithoutMonitor(nativeTest, preRunCount); runWithoutMonitor(decorator, preRunCount); runWithoutMonitor(cglibProxy, preRunCount); runWithoutMonitor(dynamicProxy, preRunCount); //执行测试; Map<String, Test> tests = new LinkedHashMap<String, Test>(); tests.put("Native ", nativeTest); tests.put("Decorator", decorator); tests.put("Dynamic ", dynamicProxy); tests.put("Cglib ", cglibProxy); int repeatCount = 3; int runCount = 1000000; runTest(repeatCount, runCount, tests); runCount = 50000000; runTest(repeatCount, runCount, tests); } private static void runTest(int repeatCount, int runCount, Map<String, Test> tests){ System.out.println(String.format("\n==================== run test : [repeatCount=%s] [runCount=%s] [java.version=%s] ====================", repeatCount, runCount, System.getProperty("java.version"))); for (int i = 0; i < repeatCount; i++) { System.out.println(String.format("\n--------- test : [%s] ---------", (i+1))); for (String key : tests.keySet()) { runWithMonitor(tests.get(key), runCount, key); } } } private static void runWithoutMonitor(Test test, int runCount) { for (int i = 0; i < runCount; i++) { test.test(i); } } private static void runWithMonitor(Test test, int runCount, String tag) { long start = System.currentTimeMillis(); for (int i = 0; i < runCount; i++) { test.test(i); } long end = System.currentTimeMillis(); System.out.println("["+tag + "] Elapsed Time:" + (end-start) + "ms"); } }
测试用例分别在 jdk6、 jdk7、jdk8 下进行了测试,每次测试分别以 1,000,000 和 50,000,000 循环次数调用 test 方法,并重复3次。
- jdk6 下的测试结果如下:
==================== run test : [repeatCount=3] [runCount=1000000] [java.version=1.6.0_45] ==================== --------- test : [1] --------- [Native ] Elapsed Time:2ms [Decorator] Elapsed Time:12ms [Dynamic ] Elapsed Time:31ms [Cglib ] Elapsed Time:31ms --------- test : [2] --------- [Native ] Elapsed Time:7ms [Decorator] Elapsed Time:7ms [Dynamic ] Elapsed Time:31ms [Cglib ] Elapsed Time:27ms --------- test : [3] --------- [Native ] Elapsed Time:7ms [Decorator] Elapsed Time:6ms [Dynamic ] Elapsed Time:23ms [Cglib ] Elapsed Time:29ms ==================== run test : [repeatCount=3] [runCount=50000000] [java.version=1.6.0_45] ==================== --------- test : [1] --------- [Native ] Elapsed Time:212ms [Decorator] Elapsed Time:226ms [Dynamic ] Elapsed Time:1054ms [Cglib ] Elapsed Time:830ms --------- test : [2] --------- [Native ] Elapsed Time:184ms [Decorator] Elapsed Time:222ms [Dynamic ] Elapsed Time:1020ms [Cglib ] Elapsed Time:826ms --------- test : [3] --------- [Native ] Elapsed Time:184ms [Decorator] Elapsed Time:208ms [Dynamic ] Elapsed Time:979ms [Cglib ] Elapsed Time:832ms
测试结果表明:jdk6 下,在运行次数较少的情况下,jdk动态代理与 cglib 差距不明显,甚至更快一些;而当调用次数增加之后, cglib 表现稍微更快一些,然而仅仅是“稍微”好一些,远没达到 10 倍差距。
- jdk7 下的测试结果如下:
==================== run test : [repeatCount=3] [runCount=1000000] [java.version=1.7.0_60] ==================== --------- test : [1] --------- [Native ] Elapsed Time:2ms [Decorator] Elapsed Time:12ms [Dynamic ] Elapsed Time:19ms [Cglib ] Elapsed Time:26ms --------- test : [2] --------- [Native ] Elapsed Time:3ms [Decorator] Elapsed Time:5ms [Dynamic ] Elapsed Time:17ms [Cglib ] Elapsed Time:20ms --------- test : [3] --------- [Native ] Elapsed Time:4ms [Decorator] Elapsed Time:4ms [Dynamic ] Elapsed Time:13ms [Cglib ] Elapsed Time:27ms ==================== run test : [repeatCount=3] [runCount=50000000] [java.version=1.7.0_60] ==================== --------- test : [1] --------- [Native ] Elapsed Time:208ms [Decorator] Elapsed Time:210ms [Dynamic ] Elapsed Time:551ms [Cglib ] Elapsed Time:923ms --------- test : [2] --------- [Native ] Elapsed Time:238ms [Decorator] Elapsed Time:210ms [Dynamic ] Elapsed Time:483ms [Cglib ] Elapsed Time:872ms --------- test : [3] --------- [Native ] Elapsed Time:217ms [Decorator] Elapsed Time:208ms [Dynamic ] Elapsed Time:494ms [Cglib ] Elapsed Time:881ms
测试结果表明:jdk7 下,情况发生了逆转!在运行次数较少(1,000,000)的情况下,jdk动态代理比 cglib 快了差不多30%;而当调用次数增加之后(50,000,000), 动态代理比 cglib 快了接近1倍。
接下来再看看jdk8下的表现如何。
- jdk8 下的测试结果如下:
==================== run test : [repeatCount=3] [runCount=1000000] [java.version=1.8.0_05] ==================== --------- test : [1] --------- [Native ] Elapsed Time:5ms [Decorator] Elapsed Time:11ms [Dynamic ] Elapsed Time:27ms [Cglib ] Elapsed Time:52ms --------- test : [2] --------- [Native ] Elapsed Time:4ms [Decorator] Elapsed Time:6ms [Dynamic ] Elapsed Time:11ms [Cglib ] Elapsed Time:24ms --------- test : [3] --------- [Native ] Elapsed Time:4ms [Decorator] Elapsed Time:5ms [Dynamic ] Elapsed Time:9ms [Cglib ] Elapsed Time:26ms ==================== run test : [repeatCount=3] [runCount=50000000] [java.version=1.8.0_05] ==================== --------- test : [1] --------- [Native ] Elapsed Time:194ms [Decorator] Elapsed Time:211ms [Dynamic ] Elapsed Time:538ms [Cglib ] Elapsed Time:965ms --------- test : [2] --------- [Native ] Elapsed Time:194ms [Decorator] Elapsed Time:214ms [Dynamic ] Elapsed Time:503ms [Cglib ] Elapsed Time:969ms --------- test : [3] --------- [Native ] Elapsed Time:190ms [Decorator] Elapsed Time:209ms [Dynamic ] Elapsed Time:495ms [Cglib ] Elapsed Time:939ms
测试结果表明:jdk8 下,延续了 JDK7 下的惊天大逆转!不过还观察另外有一个细微的变化,从绝对值来看 cglib 在 jdk8 下的表现似乎比 jdk7 还要差一点点,尽管只是一点点,但经过反复多次的执行仍然是这个趋势(注:这个趋势的结论并不严谨,只是捎带一提,如需得出结论还需进行更多样的对比实验)。
结论:从 jdk6 到 jdk7、jdk8 ,动态代理的性能得到了显著的提升,而 cglib 的表现并未跟上,甚至可能会略微下降。传言的 cglib 比 jdk动态代理高出 10 倍的情况也许是出现在更低版本的 jdk 上吧。
以上测试用例虽然简单,但揭示了 jdk 版本升级可能会带来一些新技术改变,会使我们以前的经验失效。放在真实业务场景下时,还需要按照实际情况进行测试后才能得出特定于场景的结论。
总之,实践出真知,还要与时俱进地去检视更新一些以往经验。
注:上述实验中 cglib 的版本是 3.1 。
================================================================================
http://www.thinksaas.cn/group/topic/130731/
新项目开始之前领导让研究下公司原有的框架(基于struts1.2.9+spring2.0.6),比较古老了。读service基类时发现竟然将request穿透到了service层(request为BaseService的实例变量),这样service就变成了有状态Bean,使service层变成了非线程安全,导致用Spring容器管理service的时候不得不使用prototype的scope 我们知道,service由于要做事务的包装,需要创建代理对象,spring中使用JDK动态代理或者CGLIB动态代理来创建代理对象,据说JDK动态代理创建对象的时间快于CGLIB,但是性能比CGLIB差(接下来我会测试这个观点),所以我得出以下结论: 1.spring在bean的scope为prototype的情况下,因为是延迟实例化bean,所以最好使用JDK的API创建代理对象; 2.反之对于singleton对象,spring默认是容器启动时就初始化bean,最好使用CGLIB来创建对象 把service配置成singleton我觉得性能方面显然要更好些,如果非要将request穿透到service层,是不是可以考虑用ThreadLocal? 在测试之前,我们先确定spring是以什么方式使用JDK动态代理和CGLIB的,如图: JDK动态代理:CGLIB动态代理:
接下来对测试下JDK动态代理和CGLIB动态代理的性能(CGLIB测试代码也和spring一样使用MethodInterceptor) 先贴上测试代码
public interface CountService {
int count();
}
public class CountServiceImpl implements CountService {
private int count = 0;
public int count() {
return ++count;
}
}
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.DecimalFormat;
import net.sf.cglib.core.DefaultGeneratorStrategy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
@SuppressWarnings("unused")
public class DynamicProxyPerformanceTest {
public static void main(String[] args) throws Exception {
CountService delegate = new CountServiceImpl();
long time = System.currentTimeMillis();
CountService jdkProxy = createJdkDynamicProxy(delegate);
time = System.currentTimeMillis() - time;
System.out.println("Create JDK Proxy:"+ time +"ms");
time = System.currentTimeMillis();
CountService cglibProxy = createCglibDynamicProxy(delegate);
time = System.currentTimeMillis() - time;
System.out.println("Create CGLIB Proxy:"+ time +"ms");
for (int i = 0; i < 3; i++) {
test(jdkProxy,"Run JDK Proxy:");
test(cglibProxy,"Run CGLIB Proxy:");
System.out.println("-------------------");
}
}
private static void test(CountService service, String label) throws Exception {
service.count(); // warm up
int count = 10000000;
long time = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
service.count();
}
time = System.currentTimeMillis() - time;
System.out.println(label + time +"ms,"+ new DecimalFormat().format(count * 1000 / time) +"t/s");
}
private static CountService createJdkDynamicProxy(final CountService delegate) {
CountService jdkProxy = (CountService) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[] { CountService.class }, new JdkHandler(delegate));
// 反汇编字节码用,测试的时候注释掉这段代码,不然影响测试结果
// 下面一行代码参照java.lang.reflect.Proxy
// byte[] proxyClassFile =
// sun.misc.ProxyGenerator.generateProxyClass(
// jdkProxy.getClass().getName(), jdkProxy.getClass().getInterfaces());
// try {
// FileOutputStream fos =
// new FileOutputStream(new File(jdkProxy.getClass().getName() +".class"));
// fos.write(proxyClassFile, 0, proxyClassFile.length);
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// } catch (IOException e) {
// e.printStackTrace();
// }
return jdkProxy;
}
private static class JdkHandler implements InvocationHandler {
final Object delegate;
JdkHandler(Object delegate) {
this.delegate = delegate;
}
public Object invoke(Object object, Method method, Object[] objects) throws Throwable {
return method.invoke(delegate, objects);
}
}
private static CountService createCglibDynamicProxy(final CountService delegate) throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CountServiceImpl.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
CountServiceImpl cglibProxy = (CountServiceImpl) enhancer.create();
// 反汇编字节码用,测试的时候注释掉这段代码,不然影响测试结果
// 下面一行代码参照net.sf.cglib.core.AbstractClassGenerator类中byte[] b = strategy.generate(this);
// byte[] proxyClassFile = new DefaultGeneratorStrategy().generate(enhancer);
// try {
// FileOutputStream fos =
// new FileOutputStream(new File(cglibProxy.getClass().getName() +".class"));
// fos.write(proxyClassFile, 0, proxyClassFile.length);
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// } catch (IOException e) {
// e.printStackTrace();
// }
return cglibProxy;
}
}
数据为执行三次,每次调用一千万次代理方法的结果
测试环境1:
JDK:fastdebug1.6 CGLIB:和spring2.0.6 使用同样的cglib-nodep-2.1_3.jar CPU:P8400 2.53GHz 2.53GHz 测试结果1:
Create JDK Proxy: 13 ms
Create CGLIB Proxy: 201 ms
Run JDK Proxy: 1571 ms, 897,559 t/s
Run CGLIB Proxy: 824 ms, 1,711,244 t/s
-------------------
Run JDK Proxy: 1519 ms, 928,285 t/s
Run CGLIB Proxy: 576 ms, 2,448,030 t/s
-------------------
Run JDK Proxy: 1546 ms, 912,073 t/s
Run CGLIB Proxy: 590 ms, 2,389,941 t/s
-------------------
CGLIB创建代理对象速度大概比JDK Proxy慢15倍,执行速度是JDK Proxy的2倍左右
测试环境2:
JDK:fastdebug1.7 CGLIB:和spring2.0.6 使用同样的cglib-nodep-2.1_3.jar CPU:P8400 2.53GHz 2.53GHz 测试结果2:
Create JDK Proxy: 14 ms
Create CGLIB Proxy: 204 ms
Run JDK Proxy: 1608 ms, 876,906 t/s
Run CGLIB Proxy: 529 ms, 2,665,530 t/s
-------------------
Run JDK Proxy: 1591 ms, 886,276 t/s
Run CGLIB Proxy: 405 ms, 3,481,642 t/s
-------------------
Run JDK Proxy: 1624 ms, 868,266 t/s
Run CGLIB Proxy: 405 ms, 3,481,642 t/s
-------------------
CGLIB创建代理对象速度大概比JDK Proxy慢15倍,执行速度是JDK Proxy的4倍左右
测试环境3:
JDK:jdk1.6.0_21 CGLIB:和spring2.0.6 使用同样的cglib-nodep-2.1_3.jar CPU:P8400 2.53GHz 2.53GHz 测试结果3:
Create JDK Proxy: 8 ms
Create CGLIB Proxy: 99 ms
Run JDK Proxy: 911 ms, 1,547,821 t/s
Run CGLIB Proxy: 435 ms, 3,241,529 t/s
-------------------
Run JDK Proxy: 870 ms, 1,620,764 t/s
Run CGLIB Proxy: 399 ms, 3,533,998 t/s
-------------------
Run JDK Proxy: 894 ms, 1,577,254 t/s
Run CGLIB Proxy: 404 ms, 3,490,260 t/s
-------------------
CGLIB创建代理对象速度大概比JDK Proxy慢10倍以上,执行速度是JDK Proxy的2倍左右
测试环境4:
JDK:jdk1.7.0_02 CGLIB:和spring2.0.6 使用同样的cglib-nodep-2.1_3.jar CPU:P8400 2.53GHz 2.53GHz 测试结果4:
Create JDK Proxy: 43 ms
Create CGLIB Proxy: 129 ms
Run JDK Proxy: 940 ms, 1,500,069 t/s
Run CGLIB Proxy: 299 ms, 4,715,937 t/s
-------------------
Run JDK Proxy: 921 ms, 1,531,015 t/s
Run CGLIB Proxy: 269 ms, 5,241,878 t/s
-------------------
Run JDK Proxy: 932 ms, 1,512,945 t/s
Run CGLIB Proxy: 265 ms, 5,321,001 t/s
-------------------
CGLIB创建代理对象速度大概比JDK Proxy慢3倍,执行速度是JDK Proxy的3倍以上
字节码比较:
把测试代码中被注释的部分打开,生成class文件后执行javap -c 类名
JDK动态代理生成的字节码
:
public final int count() throws ;
Code:
0: aload_0
1: getfield #16 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;
4: aload_0
5: getstatic #50 // Field m3:Ljava/lang/reflect/Method;
8: aconst_null
9: invokeinterface #28, 4 // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
14: checkcast #52 // class java/lang/Integer
17: invokevirtual #55 // Method java/lang/Integer.intValue:()I
20: ireturn
21: athrow
22: astore_1
23: new #42 // class java/lang/reflect/UndeclaredThrowableException
26: dup
27: aload_1
28: invokespecial #45 // Method java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwable;)V
31: athrow
Exception table:
from to target type
0 21 21 Class java/lang/Error
0 21 21 Class java/lang/RuntimeException
0 21 22 Class java/lang/Throwable
CGLIB生成的字节码:
public final int count();
Code:
0: aload_0
1: getfield #37 // Field CGLIB$CALLBACK_0:Lnet/sf/cglib/proxy/MethodInterceptor;
4: dup
5: ifnonnull 17
8: pop
9: aload_0
10: invokestatic #41 // Method CGLIB$BIND_CALLBACKS:(Ljava/lang/Object;)V
13: aload_0
14: getfield #37 // Field CGLIB$CALLBACK_0:Lnet/sf/cglib/proxy/MethodInterceptor;
17: dup
18: ifnull 52
21: aload_0
22: getstatic #43 // Field CGLIB$count$0$Method:Ljava/lang/reflect/Method;
25: getstatic #45 // Field CGLIB$emptyArgs:[Ljava/lang/Object;
28: getstatic #47 // Field CGLIB$count$0$Proxy:Lnet/sf/cglib/proxy/MethodProxy;
31: invokeinterface #53, 5 // InterfaceMethod net/sf/cglib/proxy/MethodInterceptor.intercept:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;Lnet/sf/cglib/proxy/MethodProxy;)Ljava/lang/Object;
36: dup
37: ifnonnull 45
40: pop
41: iconst_0
42: goto 51
45: checkcast #55 // class java/lang/Number
48: invokevirtual #58 // Method java/lang/Number.intValue:()I
51: ireturn
52: aload_0
53: invokespecial #35 // Method CountServiceImpl.count:()I
56: ireturn