幾個動態代理Proxy工具效能比較

banq發表於2009-10-16
動態代理現在基本是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 class="indent">


測試結果如下:

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修改過]

相關文章