几个动态代理Proxy工具性能比较

09-10-16 banq
    

动态代理现在基本是Java技术的核心模式,AOP模式的主要实现方式。现在我们使用Spring EJB3 Hibernate等大部分框架都有动态代理在其中,只不过表现方式在使用时并不明显。

JDK本身有提供动态代理的API,但是因为性能或功能不够突出,所以,这些框架就是要专门字节码库Code Generation Library,Code Generation Library,目前主要有CGLib (基于ASM)和Javassist。

现在JDK 6.0版本性能比以前1.4有很大提高,最近我使用这篇文章中Why do you think CGLib proxies are faster than JDK Proxies?的测试代码分别对JDK和CGLib Javassist三个动态代理性能进行了测试,发现惊人的结果,并不是说字节码库性能肯定比JDK快,至少Javassist慢。

JDK与CGLib 测试代码在TSS文章中有,我改为Javassist和JDK测试如下:

public class ProxyPerformanceComparison2 {
	public static void main(String[] args) throws Exception {
		Callable<Integer> jdkProxy = (Callable<Integer>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] { Callable.class },
				new JdkHandler(new Counter()));

		ProxyFactory f = new ProxyFactory();
		f.setInterfaces(new Class[] { Callable.class });
		Class c = f.createClass();

		Callable<Integer> cglibProxy = (Callable<Integer>) c.newInstance();
		((ProxyObject) cglibProxy).setHandler(new JavaAssitInterceptor(new Counter()));

		for (int i2 = 0; i2 < 10; i2++) {
			iterate(jdkProxy, "JDK Proxy: ");
			iterate(cglibProxy, "JAVAASSIST:     ");

			System.err.println();
		}
	}

	static final DecimalFormat format = new DecimalFormat();

	static void iterate(Callable<Integer> callable, String label) throws Exception {
		int count = 10000000;
		long time = System.currentTimeMillis();
		int total = 0;
		for (int i = 0; i < count; i++) {
			total += callable.call();
		}
		time = System.currentTimeMillis() - time;
		System.err.println(label + format.format(count * 1000 / time) + " calls/s");
	}

	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);
		}
	}

	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);
		}
	}

	static class Counter implements Callable<Integer> {
		int count = 0;

		public Integer call() throws Exception {
			return count++;
		}
	}
}

<p>

测试结果如下:

JDK 6和CGLib cglib-nodep-2.2.jar对比结果:

JDK Proxy: 1,049,937 calls/s

CGLIB: 2,820,130 calls/s

如果使用cglib以前版本,性能更快:

JDK Proxy: 1,037,575 calls/s

CGLIB: 3,112,727 calls/s

而JDK 6和JavaAssit 3.11测试结果如下:

JDK Proxy: 1,037,575 calls/s

JAVAASSIST: 626,695 calls/s

JAVAASSIST竟然慢于JDK 6,惊人。

要命的是,Hibernate 3.3以后版本使用JavaAssit,因为两家都是JBoss公司的缘故吧,http://opensource.atlassian.com/projects/hibernate/browse/HHH-2506,看来Hibernate这个好东西被收购后,开始有问题了。

Tapstry5也是使用JAVAASSIST,Javassist vs. Every Other Bytecode Library Out There

优化性能的每一步,点点积累,就会形成大效果。

[该贴被banq于2009-10-16 13:02修改过]

    

2
banq
2009-10-16 13:38

鉴于CGLIB性能优于JDK6近三倍,我在Jdon框架中引入CGLIB替代JDK,测试有一次惊人,两者竟然差不多,这主要是因为Jdon框架使用Session缓存的原因,Jmeter结果如下:

JDK结果

Label,# Samples,Average,Median,90% Line,Min,Max,Error %,Throughput,KB/sec

HTTP请求,5891,29,25,45,7,342,0.0,164.90776250594854,488.7647062554237

总体,5891,29,25,45,7,342,0.0,164.90776250594854,488.7647062554237

平均是25

而CGLIB结果:

Label,# Samples,Average,Median,90% Line,Min,Max,Error %,Throughput,KB/sec

HTTP请求,5070,30,26,47,7,240,0.0,162.87063509910374,482.72693117751936

总体,5070,30,26,47,7,240,0.0,162.87063509910374,482.72693117751936

平均是26