转于自己在公司的Blog:
http://pt./wp/experience_1054/dynamic_proxy_performance.html
因服务框架需要用动态代理生成客户端接口的stub,所以做了一下性能评测,
动态代理工具比较成熟的产品有:
JDK自带的,ASM,CGLIB(基于ASM包装),JAVAASSIST,
使用的版本分别为:
JDK-1.6.0_18-b07, ASM-3.3, CGLIB-2.2, JAVAASSIST-3.11.0.GA
(一) 测试结果:
数据为执行三次,每次调用一千万次代理方法的结果,测试代码后面有贴出。
(1) PC机测试结果:Linux 2.6.9-42.ELsmp(32bit), 2 Cores CPU(Intel Pentium4 3.06GHz)
- Create JDK Proxy: 13 ms
- Create CGLIB Proxy: 217 ms
- Create JAVAASSIST Proxy: 99 ms
- Create JAVAASSIST Bytecode Proxy: 168 ms
- Create ASM Proxy: 3 ms
- ================
- Run JDK Proxy: 2224 ms, 634,022 t/s
- Run CGLIB Proxy: 1123 ms, 1,255,623 t/s
- Run JAVAASSIST Proxy: 3212 ms, 438,999 t/s
- Run JAVAASSIST Bytecode Proxy: 206 ms, 6,844,977 t/s
- Run ASM Bytecode Proxy: 209 ms, 6,746,724 t/s
- ----------------
- Run JDK Proxy: 2169 ms, 650,099 t/s
- Run CGLIB Proxy: 1059 ms, 1,331,506 t/s
- Run JAVAASSIST Proxy: 3328 ms, 423,697 t/s
- Run JAVAASSIST Bytecode Proxy: 202 ms, 6,980,521 t/s
- Run ASM Bytecode Proxy: 206 ms, 6,844,977 t/s
- ----------------
- Run JDK Proxy: 2174 ms, 648,604 t/s
- Run CGLIB Proxy: 1032 ms, 1,366,342 t/s
- Run JAVAASSIST Proxy: 3119 ms, 452,088 t/s
- Run JAVAASSIST Bytecode Proxy: 207 ms, 6,811,910 t/s
- Run ASM Bytecode Proxy: 207 ms, 6,811,910 t/s
- ----------------
(2) 服务器测试结果:Linux 2.6.18-128.el5xen(64bit), 16 Cores CPU(Intel Xeon E5520 2.27GHz)
- Create JDK Proxy: 7 ms
- Create CGLIB Proxy: 86 ms
- Create JAVAASSIST Proxy: 36 ms
- Create JAVAASSIST Bytecode Proxy: 57 ms
- Create ASM Proxy: 1 ms
- ================
- Run JDK Proxy: 235 ms, 6,000,278 t/s
- Run CGLIB Proxy: 234 ms, 6,025,920 t/s
- Run JAVAASSIST Proxy: 459 ms, 3,072,037 t/s
- Run JAVAASSIST Bytecode Proxy: 71 ms, 19,860,076 t/s
- Run ASM Bytecode Proxy: 72 ms, 19,584,241 t/s
- ----------------
- Run JDK Proxy: 298 ms, 4,731,763 t/s
- Run CGLIB Proxy: 134 ms, 10,522,876 t/s
- Run JAVAASSIST Proxy: 406 ms, 3,473,067 t/s
- Run JAVAASSIST Bytecode Proxy: 67 ms, 21,045,752 t/s
- Run ASM Bytecode Proxy: 66 ms, 21,364,627 t/s
- ----------------
- Run JDK Proxy: 282 ms, 5,000,231 t/s
- Run CGLIB Proxy: 133 ms, 10,601,995 t/s
- Run JAVAASSIST Proxy: 406 ms, 3,473,067 t/s
- Run JAVAASSIST Bytecode Proxy: 67 ms, 21,045,752 t/s
- Run ASM Bytecode Proxy: 67 ms, 21,045,752 t/s
- ----------------
(二) 测试结论:
1. ASM和JAVAASSIST字节码生成方式不相上下,都很快,是CGLIB的5倍。
2. CGLIB次之,是JDK自带的两倍。
3. JDK自带的再次之,因JDK1.6对动态代理做了优化,如果用低版本JDK更慢,要注意的是JDK也是通过字节码生成来实现动态代理的,而不是反射。
4. JAVAASSIST提供者动态代理接口最慢,比JDK自带的还慢。
(这也是为什么网上有人说JAVAASSIST比JDK还慢的原因,用JAVAASSIST最好别用它提供的动态代理接口,而可以考虑用它的字节码生成方式)
(三) 差异原因:
各方案生成的字节码不一样,
像JDK和CGLIB都考虑了很多因素,以及继承或包装了自己的一些类,
所以生成的字节码非常大,而我们很多时候用不上这些,
而手工生成的字节码非常小,所以速度快,
具体的字节码对比,后面有贴出,可自行分析。
(四) 最终选型:
最终决定使用JAVAASSIST的字节码生成代理方式,
虽然ASM稍快,但并没有快一个数量级,
而JAVAASSIST的字节码生成方式比ASM方便,
JAVAASSIST只需用字符串拼接出Java源码,便可生成相应字节码,
而ASM需要手工写字节码。
(五) 测试代码:
- public interface CountService {
-
- int count();
-
- }
- public class CountServiceImpl implements CountService {
-
- private int count = 0;
-
- public int count() {
- return count ++;
- }
- }
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.text.DecimalFormat;
-
- import javassist.ClassPool;
- import javassist.CtClass;
- import javassist.CtField;
- import javassist.CtNewConstructor;
- import javassist.CtNewMethod;
- import javassist.util.proxy.MethodHandler;
- import javassist.util.proxy.ProxyFactory;
- import javassist.util.proxy.ProxyObject;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
-
- import org.objectweb.asm.ClassWriter;
- import org.objectweb.asm.FieldVisitor;
- import org.objectweb.asm.MethodVisitor;
- import org.objectweb.asm.Opcodes;
-
- 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");
-
- time = System.currentTimeMillis();
- CountService javassistProxy = createJavassistDynamicProxy(delegate);
- time = System.currentTimeMillis() - time;
- System.out.println("Create JAVAASSIST Proxy: " + time + " ms");
-
- time = System.currentTimeMillis();
- CountService javassistBytecodeProxy = createJavassistBytecodeDynamicProxy(delegate);
- time = System.currentTimeMillis() - time;
- System.out.println("Create JAVAASSIST Bytecode Proxy: " + time + " ms");
-
- time = System.currentTimeMillis();
- CountService asmBytecodeProxy = createAsmBytecodeDynamicProxy(delegate);
- time = System.currentTimeMillis() - time;
- System.out.println("Create ASM Proxy: " + time + " ms");
- System.out.println("================");
-
- for (int i = 0; i < 3; i++) {
- test(jdkProxy, "Run JDK Proxy: ");
- test(cglibProxy, "Run CGLIB Proxy: ");
- test(javassistProxy, "Run JAVAASSIST Proxy: ");
- test(javassistBytecodeProxy, "Run JAVAASSIST Bytecode Proxy: ");
- test(asmBytecodeProxy, "Run ASM Bytecode Proxy: ");
- System.out.println("----------------");
- }
- }
-
- private static void test(CountService service, String label)
- throws Exception {
- service.count();
- 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));
- 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.setCallback(new CglibInterceptor(delegate));
- enhancer.setInterfaces(new Class[] { CountService.class });
- CountService cglibProxy = (CountService) enhancer.create();
- return cglibProxy;
- }
-
- private static class CglibInterceptor implements MethodInterceptor {
-
- final Object delegate;
-
- CglibInterceptor(Object delegate) {
- this.delegate = delegate;
- }
-
- public Object intercept(Object object, Method method, Object[] objects,
- MethodProxy methodProxy) throws Throwable {
- return methodProxy.invoke(delegate, objects);
- }
- }
-
- private static CountService createJavassistDynamicProxy(final CountService delegate) throws Exception {
- ProxyFactory proxyFactory = new ProxyFactory();
- proxyFactory.setInterfaces(new Class[] { CountService.class });
- Class<?> proxyClass = proxyFactory.createClass();
- CountService javassistProxy = (CountService) proxyClass.newInstance();
- ((ProxyObject) javassistProxy).setHandler(new JavaAssitInterceptor(delegate));
- return javassistProxy;
- }
-
- private static class JavaAssitInterceptor implements MethodHandler {
-
- final Object delegate;
-
- JavaAssitInterceptor(Object delegate) {
- this.delegate = delegate;
- }
-
- public Object invoke(Object self, Method m, Method proceed,
- Object[] args) throws Throwable {
- return m.invoke(delegate, args);
- }
- }
-
- private static CountService createJavassistBytecodeDynamicProxy(CountService delegate) throws Exception {
- ClassPool mPool = new ClassPool(true);
- CtClass mCtc = mPool.makeClass(CountService.class.getName() + "JavaassistProxy");
- mCtc.addInterface(mPool.get(CountService.class.getName()));
- mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
- mCtc.addField(CtField.make("public " + CountService.class.getName() + " delegate;", mCtc));
- mCtc.addMethod(CtNewMethod.make("public int count() { return delegate.count(); }", mCtc));
- Class<?> pc = mCtc.toClass();
- CountService bytecodeProxy = (CountService) pc.newInstance();
- Field filed = bytecodeProxy.getClass().getField("delegate");
- filed.set(bytecodeProxy, delegate);
- return bytecodeProxy;
- }
-
- private static CountService createAsmBytecodeDynamicProxy(CountService delegate) throws Exception {
- ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
- String className = CountService.class.getName() + "AsmProxy";
- String classPath = className.replace('.', '/');
- String interfacePath = CountService.class.getName().replace('.', '/');
- classWriter.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, classPath, null, "java/lang/Object", new String[] {interfacePath});
-
- MethodVisitor initVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
- initVisitor.visitCode();
- initVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- initVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
- initVisitor.visitInsn(Opcodes.RETURN);
- initVisitor.visitMaxs(0, 0);
- initVisitor.visitEnd();
-
- FieldVisitor fieldVisitor = classWriter.visitField(Opcodes.ACC_PUBLIC, "delegate", "L" + interfacePath + ";", null, null);
- fieldVisitor.visitEnd();
-
- MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "count", "()I", null, null);
- methodVisitor.visitCode();
- methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- methodVisitor.visitFieldInsn(Opcodes.GETFIELD, classPath, "delegate", "L" + interfacePath + ";");
- methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, interfacePath, "count", "()I");
- methodVisitor.visitInsn(Opcodes.IRETURN);
- methodVisitor.visitMaxs(0, 0);
- methodVisitor.visitEnd();
-
- classWriter.visitEnd();
- byte[] code = classWriter.toByteArray();
- CountService bytecodeProxy = (CountService) new ByteArrayClassLoader().getClass(className, code).newInstance();
- Field filed = bytecodeProxy.getClass().getField("delegate");
- filed.set(bytecodeProxy, delegate);
- return bytecodeProxy;
- }
-
- private static class ByteArrayClassLoader extends ClassLoader {
-
- public ByteArrayClassLoader() {
- super(ByteArrayClassLoader.class.getClassLoader());
- }
-
- public synchronized Class<?> getClass(String name, byte[] code) {
- if (name == null) {
- throw new IllegalArgumentException("");
- }
- return defineClass(name, code, 0, code.length);
- }
-
- }
- }
(六) 字节码对比
(1) JDK生成的字节码:
(2) CGLIB生成的字节码:
(3) JAVAASSIST动态代理接口生成的字节码:
(5) JAVAASSIST拼接源码生成的字节码:
- public class com.alibaba.test.performance.dynamicproxy.CountServiceJavaassistProxy extends java.lang.Object implements com.alibaba.test.performance.dynamicproxy.CountService{
- public com.alibaba.test.performance.dynamicproxy.CountService delegate;
-
- public com.alibaba.test.performance.dynamicproxy.CountServiceJavaassistProxy();
- Code:
- 0: aload_0
- 1: invokespecial #12;
- 4: return
-
- public int count();
- Code:
- 0: aload_0
- 1: getfield #19;
- 4: invokeinterface #21, 1;
- 9: ireturn
-
- }
(6) 用ASM自行生成的字节码:
- public class com.alibaba.test.performance.dynamicproxy.CountServiceAsmProxy extends java.lang.Object implements com.alibaba.test.performance.dynamicproxy.CountService{
- public com.alibaba.test.performance.dynamicproxy.CountService delegate;
-
- public com.alibaba.test.performance.dynamicproxy.CountServiceAsmProxy();
- Code:
- 0: aload_0
- 1: invokespecial #10;
- 4: return
-
- public int count();
- Code:
- 0: aload_0
- 1: getfield #16;
- 4: invokeinterface #18, 1;
- 9: ireturn
-
- }
|