说明
这里将cglib 和 jdk动态代理进行对比,毕竟工业界用java assist或者asm比较奇怪
针对调用速度进行比较(创建速度jdk动态代理肯定要快)
版本:cglib 2.2.2 jdk 1.8.0_77
主要参考了下面这几篇文章,各自说法各不同,让人比较奇怪
http://www.cnblogs.com/haiq/p/4304615.html
http://adolphor.com/blog/2016/12/14/java-proxy-performance.html
第一篇结论就是jdk7以后 jdk动态代理 比 cglib快
第二篇结论就是cglib比 jdk动态代理快
实验了一下,两份代码,在我的win7上
结果都相符,第一篇结论也是对的,第二篇结论也是对的。
实时是这样吗。
不,参考两个cglib实现的源码,略有不同。
不同体现在
实验代码对比
//说cglib慢的
Test cglibProxy = CglibProxyTest.newProxyInstance(TestImpl.class);
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);
}
可以看到这是类似前面demo的写法,用的 setSuperclass 以及 invokeSuper这种方式
//说cglib快的
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 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;
}
CountService delegate = new CountServiceImpl();
CountService cglibProxy = createCglibDynamicProxy(delegate);
看到区别没有!!!
这种方式并没有setSuperClass以及invokeSuper
而是setInterfaces以及invoke
而是传递了一个实现类,实现类的fastclass
(上一节分析了invoke和invokeSuper的区别,invoke必须传入''另外一个obj",否则会导致死循环)
实验结果分析
为什么两份代码结果差距那么大
cglib快的那一份代码,用的invoke,也就是上一节源码分析中的f1(即实现类的fastclass),对应的class decompile如下
//cglib快的那一份代码中的f1,也就是
package cglib;
import cglib.CglibLearn;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;
public class CglibLearn$service$$FastClassByCGLIB$$eebe5c1
extends FastClass {
public CglibLearn$service$$FastClassByCGLIB$$eebe5c1(Class class_) {
super(class_);
}
public int getIndex(Signature signature) {
String string = signature.toString();
switch (string.hashCode()) {
case -909388886: {
if (!string.equals("say()V")) break;
return 0;
}
}
return -1;
}
public int getIndex(String string, Class[] arrclass) {
String string2 = string;
Class[] arrclass2 = arrclass;
switch (string2.hashCode()) {
case 113643: {
if (!string2.equals("say")) break;
arrclass2 = arrclass2;
switch (arrclass2.length) {
case 0: {
return 0;
}
}
break;
}
default: {
break;
}
}
return -1;
}
public int getIndex(Class[] arrclass) {
Class[] arrclass2 = arrclass;
Class[] arrclass3 = arrclass2;
arrclass2.length;
return -1;
}
public Object invoke(int n, Object object, Object[] arrobject) throws InvocationTargetException {
try {
switch (n) {
case 0: {
((CglibLearn.service)object).say();
return null;
}
}
}
catch (Throwable v0) {
throw new InvocationTargetException(v0);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public Object newInstance(int n, Object[] arrobject) throws InvocationTargetException {
CglibLearn.service service2;
CglibLearn.service service3 = service2;
CglibLearn.service service4 = service2;
int n2 = n;
try {
}
catch (Throwable v4) {
throw new InvocationTargetException(v4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public int getMaxIndex() {
return 0;
}
}
可以看到该fastClass里面的invoke函数非常简单,正常情况只有一种,那就是case 0;
那么,为什么invokeSuper和setSuperClass的实现方式,就会慢呢。
invokeSuper用的是f2,也就是enhance的fastClass,这里面函数就多了,稍微感受下
invokeSuper里面调用f2的invoke,要处理26种case
public Object invoke(int n, Object object, Object[] arrobject) throws InvocationTargetException {
CglibLearn$service$$EnhancerByCGLIB$$e9bfe24c cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c = (CglibLearn$service$$EnhancerByCGLIB$$e9bfe24c)object;
try {
switch (n) {
case 0: {
return new Boolean(cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.equals(arrobject[0]));
}
case 1: {
return cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.toString();
}
case 2: {
return new Integer(cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.hashCode());
}
case 3: {
return cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.newInstance((Class[])arrobject[0], (Object[])arrobject[1], (Callback[])arrobject[2]);
}
case 4: {
return cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.newInstance((Callback[])arrobject[0]);
}
case 5: {
return cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.newInstance((Callback)arrobject[0]);
}
case 6: {
cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.setCallbacks((Callback[])arrobject[0]);
return null;
}
case 7: {
cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.setCallback(((Number)arrobject[0]).intValue(), (Callback)arrobject[1]);
return null;
}
case 8: {
cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.say();
return null;
}
case 9: {
CglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.CGLIB$SET_THREAD_CALLBACKS((Callback[])arrobject[0]);
return null;
}
case 10: {
CglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.CGLIB$SET_STATIC_CALLBACKS((Callback[])arrobject[0]);
return null;
}
case 11: {
return cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.getCallback(((Number)arrobject[0]).intValue());
}
case 12: {
return cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.getCallbacks();
}
case 13: {
return CglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.CGLIB$findMethodProxy((Signature)arrobject[0]);
}
case 14: {
CglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.CGLIB$STATICHOOK1();
return null;
}
case 15: {
cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.CGLIB$finalize$0();
return null;
}
case 16: {
return new Boolean(cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.CGLIB$equals$1(arrobject[0]));
}
case 17: {
return cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.CGLIB$toString$2();
}
case 18: {
return new Integer(cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.CGLIB$hashCode$3());
}
case 19: {
return cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.CGLIB$clone$4();
}
case 20: {
cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.CGLIB$say$5();
return null;
}
case 21: {
cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.wait();
return null;
}
case 22: {
cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.wait(((Number)arrobject[0]).longValue(), ((Number)arrobject[1]).intValue());
return null;
}
case 23: {
cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.wait(((Number)arrobject[0]).longValue());
return null;
}
case 24: {
return cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.getClass();
}
case 25: {
cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.notify();
return null;
}
case 26: {
cglibLearn$service$$EnhancerByCGLIB$$e9bfe24c.notifyAll();
return null;
}
}
}
catch (Throwable v1) {
throw new InvocationTargetException(v1);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
所以cglib两种实现方式,invokeSuper + setSuperClass 永远比 invoke + setInterfaces慢
因为同样的情况下,后者生成的函数更多,后者switch判断的case也就更多
net.sf.cglib.proxy.MethodProxy#invoke比net.sf.cglib.proxy.MethodProxy#invokeSuper快
那么cglib invoke+setInterfaces方式和 jdk动态代理谁快
通过上面的例子,可以看出cglib其实和实现类的函数个数有关系,毕竟要switch各个index
自定义实验
利用java benchmark即jmh包进行测试
package cglibtest;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.TimeUnit;
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
public class DynamicProxyPerformanceTest {
static CountService jdkProxy;
static CountService cglibProxy;
static {
try {
init();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void init() throws Exception {
CountService delegate = new CountServiceImpl();
long time = System.currentTimeMillis();
jdkProxy = createJdkDynamicProxy(delegate);
time = System.currentTimeMillis() - time;
System.out.println("Create JDK Proxy: " + time + " ms");
time = System.currentTimeMillis();
cglibProxy = createCglibDynamicProxy(delegate);
time = System.currentTimeMillis() - time;
System.out.println("Create CGLIB Proxy: " + time + " ms");
}
public static void main(String[] args) throws Exception {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".//");
try {
init();
} catch (Exception e) {
e.printStackTrace();
}
Options opt = new OptionsBuilder()
.include(DynamicProxyPerformanceTest.class.getSimpleName())
.forks(1)
.warmupIterations(3)
.measurementIterations(5)
.build();
new Runner(opt).run();
}
@Benchmark
public void cglibProxy() {
int count = 1000000;
for (int i = 0; i < count; i++) {
cglibProxy.count();
cglibProxy.test1();
cglibProxy.test2();
cglibProxy.test3();
cglibProxy.test4();
cglibProxy.test5();
cglibProxy.test6();
cglibProxy.test7();
cglibProxy.test8();
cglibProxy.test9();
cglibProxy.t0();
cglibProxy.t1();
cglibProxy.t2();
cglibProxy.t3();
cglibProxy.t4();
cglibProxy.t5();
cglibProxy.t6();
cglibProxy.t7();
cglibProxy.t8();
cglibProxy.t9();
cglibProxy.a1();
cglibProxy.a2();
cglibProxy.a3();
cglibProxy.a4();
cglibProxy.a5();
cglibProxy.a6();
cglibProxy.a7();
cglibProxy.a8();
cglibProxy.a9();
}
}
@Benchmark
public void testjdkProxy() {
int count = 1000000;
for (int i = 0; i < count; i++) {
jdkProxy.count();
jdkProxy.test1();
jdkProxy.test2();
jdkProxy.test3();
jdkProxy.test4();
jdkProxy.test5();
jdkProxy.test6();
jdkProxy.test7();
jdkProxy.test8();
jdkProxy.test9();
jdkProxy.t0();
jdkProxy.t1();
jdkProxy.t2();
jdkProxy.t3();
jdkProxy.t4();
jdkProxy.t5();
jdkProxy.t6();
jdkProxy.t7();
jdkProxy.t8();
jdkProxy.t9();
jdkProxy.a1();
jdkProxy.a2();
jdkProxy.a3();
jdkProxy.a4();
jdkProxy.a5();
jdkProxy.a6();
jdkProxy.a7();
jdkProxy.a8();
jdkProxy.a9();
}
}
private static <T extends CountService> 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.setSuperclass(CountServiceImpl.class);
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);
}
}
}
CountService 提供了这些接口a0,test1()等等
CountServiceImpl实现了这些接口,什么事情都没干,主要权衡函数调用的时间
实验结果
接口提供29个函数,实验对比
Benchmark | Mode | Cnt | Score | Units |
---|---|---|---|---|
DynamicProxyPerformanceTest.cglibProxy | thrpt | 5 | 7.385 ± 0.749 | ops/s |
DynamicProxyPerformanceTest.testjdkProxy | thrpt | 5 | 6.164 ± 0.711 | ops/s |
cglib比jdk动态代理快一点
接口保留19个函数
Benchmark | Mode | Cnt | Score | Units |
---|---|---|---|---|
DynamicProxyPerformanceTest.cglibProxy | thrpt | 5 | 15.126 ± 0.730 | ops/s |
DynamicProxyPerformanceTest.testjdkProxy | thrpt | 5 | 9.790 ± 1.008 | ops/s |
cglib是jdk动态代理1.5倍速度
接口保留49个函数,平均调用
Benchmark | Mode | Cnt | Score | Units |
---|---|---|---|---|
DynamicProxyPerformanceTest.cglibProxy | thrpt | 5 | 4.987 ± 0.859 | ops/s |
DynamicProxyPerformanceTest.testjdkProxy | thrpt | 5 | 3.826 ± 0.259 | ops/s |
接口保留49个函数,只调用清单上最后一个函数
Benchmark | Mode | Cnt | Score | Units |
---|---|---|---|---|
DynamicProxyPerformanceTest.cglibProxy | thrpt | 5 | 193.406 ± 6.209 | ops/s |
DynamicProxyPerformanceTest.testjdkProxy | thrpt | 5 | 347.192 ± 26.464 | ops/s |
这个时候,java利用反射,比cglib每次都switch到一个比较靠后case快
结论
1.同样情况下,cglib两种实现方式,invokeSuper + setSuperClass 永远比 invoke + setInterfaces慢
2.cglib invoke + setInterfaces 在方法数量较少的时候,在函数平均调用的情况下 比jdkProxy快,随着函数增多,优势越来越不明显,到达某个数量级一定比jdk动态代理慢
3.cglib invoke + setInterfaces 在调用特定函数(在switch中靠后的case) 会比jdk动态代理慢
思考
cglib的瓶颈在于:
调用net.sf.cglib.reflect.FastClass#invoke(int, java.lang.Object, java.lang.Object[])时需要switch的case:
如果有n个函数,那么就要一个个比较,复杂度O(n)
这里如果有一个key -> behavior的映射就好了,目前并没有。
如果可以用asm在这里写成一个二分搜索,cglib就会快多了,变成O(log2 n),时间上就是一个飞跃,只不过这个fastclass就会看起来很丑。(目前最新3.2.5的版本也没有改动这一块)
refer
http://www.cnblogs.com/haiq/p/4304615.html
http://adolphor.com/blog/2016/12/14/java-proxy-performance.html
https://zeroturnaround.com/rebellabs/testing-the-performance-of-4-java-runtime-code-generators-cglib-javassist-jdk-proxy-byte-buddy/
https://github.com/cglib/cglib/releases/tag/RELEASE_3_2_5
http://mvnrepository.com/artifact/cglib/cglib/3.2.5