背景
问题描述
Spring Framework 提供了两种动态代理的实现,CGLIB 和 JDK 动态代理,并且提供了 proxy-target-class。
两者实现方式上并不相同,初步看来 CGLIB 性能上应该有优势,但是没有实际数据作为支撑。
从 Spring Boot 2.0 开始,默认的 AOP 策略已经更换为 CGLIB,即 proxy-target-class = true。
可以先看结论再看分析
参考来源
主要参考了如下两篇文章的数据:
两者的结论完全相反
后续主要参考了:
提供了一定思路,但是结论并不相同
情况分析
CGLIB 基于类与接口的区别
关键代码
1 2 3 4 5 6 7 8 9
| public void setSuperclass(Class superclass) { if (superclass != null && superclass.isInterface()) { this.setInterfaces(new Class[]{superclass}); } else if (superclass != null && superclass.equals(Object.class)) { this.superclass = null; } else { this.superclass = superclass; } }
|
基于类和接口生成的代理类,均继承自 FastClass,其中前者生成的代码 switch 和 case 数量均较多,可能是两种方式下性能差距的原因之一。
CGLIB 与 Method 数量的关系
CGLIB 的 invoke 基于 switch 实现,Method 数量较多的情况下,性能下降严重。
JDK 动态代理的实现
实现相对简单,代理类实现了被代理接口,并生成了 java.lang.reflect.Method。
实验内容
实验环境
- OS: 64bit Mac OS X 10.13.4
- CPU: Intel Core i7-4770HQ @ 2.20GHz
- RAM: 16GB
- JDK: Oracle JDK 10.0.1
- CGLIB: Spring 5.0.5.RELEASE 内嵌
- JMH: 1.20
实验数据
方式 |
分数 |
cglibBigClassEndProxy |
339.777 |
cglibBigClassProxy |
331.552 |
cglibBigEndProxy |
358.999 |
cglibBigProxy |
359.476 |
cglibSmallClassProxy |
578.817 |
cglibSmallProxy |
618.582 |
jdkBigEndProxy |
432.813 |
jdkBigProxy |
434.224 |
jdkSmallProxy |
432.592 |
说明
- Big:含有 50 个 Method
- Small:含有 5 个 Method
- End:使用最后的 Method,否则使用首个
- Class:使用类作为被代理的内容
实验代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
| package com.example.demo;
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 org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy;
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 {
private static final int TEST_COUNT = 50_0000; private static NopBigService jdkBigProxy; private static NopBigService cglibBigProxy; private static NopBigService cglibBigClassProxy; private static NopSmallService jdkSmallProxy; private static NopSmallService cglibSmallProxy; private static NopSmallService cglibSmallClassProxy;
private static void init() {
NopBigService nopBigService = new NopBigServiceImpl();
NopSmallService nopSmallService = new NopSmallServiceImpl(); jdkBigProxy = createJdkBigDynamicProxy(nopBigService); cglibBigProxy = createCglibBigDynamicProxy(nopBigService); cglibBigClassProxy = createCglibBigClassDynamicProxy(nopBigService); jdkSmallProxy = createJdkSmallDynamicProxy(nopSmallService); cglibSmallProxy = createCglibSmallDynamicProxy(nopSmallService); cglibSmallClassProxy = createCglibSmallClassDynamicProxy(nopSmallService);
} static { init(); }
public static void main(String[] args) throws Exception { Options opt = new OptionsBuilder() .include(DynamicProxyPerformanceTest.class.getSimpleName()) .forks(1) .warmupIterations(0) .measurementIterations(1) .build(); new Runner(opt).run(); }
@Benchmark public void jdkBigProxy() { for (int i = 0; i < TEST_COUNT; i++) { jdkBigProxy.m1(); } }
@Benchmark public void jdkBigEndProxy() { for (int i = 0; i < TEST_COUNT; i++) { jdkBigProxy.m49(); } }
@Benchmark public void jdkSmallProxy() { for (int i = 0; i < TEST_COUNT; i++) { jdkSmallProxy.m1(); } }
@Benchmark public void cglibBigProxy() { for (int i = 0; i < TEST_COUNT; i++) { cglibBigProxy.m1(); } }
@Benchmark public void cglibBigEndProxy() { for (int i = 0; i < TEST_COUNT; i++) { cglibBigProxy.m49(); } }
@Benchmark public void cglibSmallProxy() { for (int i = 0; i < TEST_COUNT; i++) { cglibSmallProxy.m1(); } }
@Benchmark public void cglibBigClassProxy() { for (int i = 0; i < TEST_COUNT; i++) { cglibBigClassProxy.m1(); } }
@Benchmark public void cglibBigClassEndProxy() { for (int i = 0; i < TEST_COUNT; i++) { cglibBigClassProxy.m49(); } }
@Benchmark public void cglibSmallClassProxy() { for (int i = 0; i < TEST_COUNT; i++) { cglibSmallClassProxy.m1(); } }
private static NopBigService createJdkBigDynamicProxy( final NopBigService delegate) { return (NopBigService) Proxy .newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{NopBigService.class}, new JdkHandler(delegate)); }
private static NopSmallService createJdkSmallDynamicProxy( final NopSmallService delegate) { return (NopSmallService) Proxy .newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{NopSmallService.class}, new JdkHandler(delegate)); }
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 NopBigService createCglibBigDynamicProxy( final NopBigService delegate) { Enhancer enhancer = new Enhancer(); enhancer.setCallback(new CglibInterceptor(delegate)); enhancer.setSuperclass(NopBigService.class); return (NopBigService) enhancer.create(); }
private static NopSmallService createCglibSmallDynamicProxy( final NopSmallService delegate) { Enhancer enhancer = new Enhancer(); enhancer.setCallback(new CglibInterceptor(delegate)); enhancer.setSuperclass(NopSmallService.class); return (NopSmallService) enhancer.create(); }
private static NopBigService createCglibBigClassDynamicProxy( final NopBigService delegate) { Enhancer enhancer = new Enhancer(); enhancer.setCallback(new CglibInterceptor(delegate)); enhancer.setSuperclass(NopBigServiceImpl.class); return (NopBigService) enhancer.create(); }
private static NopSmallService createCglibSmallClassDynamicProxy( final NopSmallService delegate) { Enhancer enhancer = new Enhancer(); enhancer.setCallback(new CglibInterceptor(delegate)); enhancer.setSuperclass(NopSmallServiceImpl.class); return (NopSmallService) enhancer.create(); }
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); } } }
|
其他
对于 CGLIB 和 JDK 动态代理,均可将生成的代理类保存,便于查看,具体方法可以自行搜索。
结论
- CGLIB 对类进行代理比接口慢,随着 Method 数量上升差距逐渐减少
- CGLIB 随着 Method 数量上升,性能出现比较严重的下降
- CGLIB 被调用 Method 的位置对于性能无影响(可能与相关优化有关)
- JDK 动态代理性能不受 Method 数量影响
- CGLIB 的性能在 Method 较少时优于 JDK 动态代理,较多时相反