• Cglib 与 JDK动态代理的运行性能比较


    都说 Cglib 创建的动态代理的运行性能比 JDK 动态代理能高出大概 10 倍,今日抱着怀疑精神验证了一下,发现情况有所不同,遂贴出实验结果,以供参考和讨论。

    代码很简单,首先,定义一个 Test 接口,和一个实现 TestImpl 。Test 接口仅定义一个方法 test,对传入的 int 参数加 1 后返回。代码如下:

    package my.test;
    
    public interface Test {
        
        public int test(int i);
        
    }
    package my.test;
    
    public class TestImpl implements Test{
        public int test(int i) {
            return i+1;
        }
    }

    然后,定义了三种代理的实现:装饰者模式实现的代理(decorator),JDK 动态代理(dynamic proxy) 和 Cglib 动态代理 (cglib proxy)。代码如下:

    package my.test;
    
    public class DecoratorTest implements Test{
        private Test target;
        
        public DecoratorTest(Test target) {
            this.target = target;
        }
    
        public int test(int i) {
            return target.test(i);
        }
    }
    package my.test;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class DynamicProxyTest implements InvocationHandler {
        private Test target;
    
        private DynamicProxyTest(Test target) {
            this.target = target;
        }
    
        public static Test newProxyInstance(Test target) {
            return (Test) Proxy
                    .newProxyInstance(DynamicProxyTest.class.getClassLoader(),
                            new Class<?>[] { Test.class },
                            new DynamicProxyTest(target));
    
        }
    
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            return method.invoke(target, args);
        }
    }
    package my.test;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibProxyTest implements MethodInterceptor {
        
        private CglibProxyTest() {
        }
        
        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);
        }
    
    }

    以 TestImpl 的调用耗时作为基准,对比通过其它三种代理进行调用的耗时。测试代码如下:

    package my.test;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    public class ProxyPerfTester {
    
        public static void main(String[] args) {
            //创建测试对象;
            Test nativeTest = new TestImpl();
            Test decorator = new DecoratorTest(nativeTest);
            Test dynamicProxy = DynamicProxyTest.newProxyInstance(nativeTest);
            Test cglibProxy = CglibProxyTest.newProxyInstance(TestImpl.class);
    
            //预热一下;
            int preRunCount = 10000;
            runWithoutMonitor(nativeTest, preRunCount);
            runWithoutMonitor(decorator, preRunCount);
            runWithoutMonitor(cglibProxy, preRunCount);
            runWithoutMonitor(dynamicProxy, preRunCount);
            
            //执行测试;
            Map<String, Test> tests = new LinkedHashMap<String, Test>();
            tests.put("Native   ", nativeTest);
            tests.put("Decorator", decorator);
            tests.put("Dynamic  ", dynamicProxy);
            tests.put("Cglib    ", cglibProxy);
            int repeatCount = 3;
            int runCount = 1000000;
            runTest(repeatCount, runCount, tests);
            runCount = 50000000;
            runTest(repeatCount, runCount, tests);
        }
        
        private static void runTest(int repeatCount, int runCount, Map<String, Test> tests){
            System.out.println(String.format("
    ==================== run test : [repeatCount=%s] [runCount=%s] [java.version=%s] ====================", repeatCount, runCount, System.getProperty("java.version")));
            for (int i = 0; i < repeatCount; i++) {
                System.out.println(String.format("
    --------- test : [%s] ---------", (i+1)));
                for (String key : tests.keySet()) {
                    runWithMonitor(tests.get(key), runCount, key);
                }
            }
        }
        
        private static void runWithoutMonitor(Test test, int runCount) {
            for (int i = 0; i < runCount; i++) {
                test.test(i);
            }
        }
        
        private static void runWithMonitor(Test test, int runCount, String tag) {
            long start = System.currentTimeMillis();
            for (int i = 0; i < runCount; i++) {
                test.test(i);
            }
            long end = System.currentTimeMillis();
            System.out.println("["+tag + "] Elapsed Time:" + (end-start) + "ms");
        }
    }

    测试用例分别在 jdk6、 jdk7、jdk8 下进行了测试,每次测试分别以 1,000,000 和 50,000,000 循环次数调用 test 方法,并重复3次。

    • jdk6 下的测试结果如下:
    ==================== run test : [repeatCount=3] [runCount=1000000] [java.version=1.6.0_45] ====================
    
    --------- test : [1] ---------
    [Native   ] Elapsed Time:2ms
    [Decorator] Elapsed Time:12ms
    [Dynamic  ] Elapsed Time:31ms
    [Cglib    ] Elapsed Time:31ms
    
    --------- test : [2] ---------
    [Native   ] Elapsed Time:7ms
    [Decorator] Elapsed Time:7ms
    [Dynamic  ] Elapsed Time:31ms
    [Cglib    ] Elapsed Time:27ms
    
    --------- test : [3] ---------
    [Native   ] Elapsed Time:7ms
    [Decorator] Elapsed Time:6ms
    [Dynamic  ] Elapsed Time:23ms
    [Cglib    ] Elapsed Time:29ms
    
    ==================== run test : [repeatCount=3] [runCount=50000000] [java.version=1.6.0_45] ====================
    
    --------- test : [1] ---------
    [Native   ] Elapsed Time:212ms
    [Decorator] Elapsed Time:226ms
    [Dynamic  ] Elapsed Time:1054ms
    [Cglib    ] Elapsed Time:830ms
    
    --------- test : [2] ---------
    [Native   ] Elapsed Time:184ms
    [Decorator] Elapsed Time:222ms
    [Dynamic  ] Elapsed Time:1020ms
    [Cglib    ] Elapsed Time:826ms
    
    --------- test : [3] ---------
    [Native   ] Elapsed Time:184ms
    [Decorator] Elapsed Time:208ms
    [Dynamic  ] Elapsed Time:979ms
    [Cglib    ] Elapsed Time:832ms

      测试结果表明:jdk6 下,在运行次数较少的情况下,jdk动态代理与 cglib 差距不明显,甚至更快一些;而当调用次数增加之后, cglib 表现稍微更快一些,然而仅仅是“稍微”好一些,远没达到 10 倍差距。

    • jdk7 下的测试结果如下:
    ==================== run test : [repeatCount=3] [runCount=1000000] [java.version=1.7.0_60] ====================
    
    --------- test : [1] ---------
    [Native   ] Elapsed Time:2ms
    [Decorator] Elapsed Time:12ms
    [Dynamic  ] Elapsed Time:19ms
    [Cglib    ] Elapsed Time:26ms
    
    --------- test : [2] ---------
    [Native   ] Elapsed Time:3ms
    [Decorator] Elapsed Time:5ms
    [Dynamic  ] Elapsed Time:17ms
    [Cglib    ] Elapsed Time:20ms
    
    --------- test : [3] ---------
    [Native   ] Elapsed Time:4ms
    [Decorator] Elapsed Time:4ms
    [Dynamic  ] Elapsed Time:13ms
    [Cglib    ] Elapsed Time:27ms
    
    ==================== run test : [repeatCount=3] [runCount=50000000] [java.version=1.7.0_60] ====================
    
    --------- test : [1] ---------
    [Native   ] Elapsed Time:208ms
    [Decorator] Elapsed Time:210ms
    [Dynamic  ] Elapsed Time:551ms
    [Cglib    ] Elapsed Time:923ms
    
    --------- test : [2] ---------
    [Native   ] Elapsed Time:238ms
    [Decorator] Elapsed Time:210ms
    [Dynamic  ] Elapsed Time:483ms
    [Cglib    ] Elapsed Time:872ms
    
    --------- test : [3] ---------
    [Native   ] Elapsed Time:217ms
    [Decorator] Elapsed Time:208ms
    [Dynamic  ] Elapsed Time:494ms
    [Cglib    ] Elapsed Time:881ms

    测试结果表明:jdk7 下,情况发生了逆转!在运行次数较少(1,000,000)的情况下,jdk动态代理比 cglib 快了差不多30%;而当调用次数增加之后(50,000,000), 动态代理比 cglib 快了接近1倍。

    接下来再看看jdk8下的表现如何。

    • jdk8 下的测试结果如下:
    ==================== run test : [repeatCount=3] [runCount=1000000] [java.version=1.8.0_05] ====================
    
    --------- test : [1] ---------
    [Native   ] Elapsed Time:5ms
    [Decorator] Elapsed Time:11ms
    [Dynamic  ] Elapsed Time:27ms
    [Cglib    ] Elapsed Time:52ms
    
    --------- test : [2] ---------
    [Native   ] Elapsed Time:4ms
    [Decorator] Elapsed Time:6ms
    [Dynamic  ] Elapsed Time:11ms
    [Cglib    ] Elapsed Time:24ms
    
    --------- test : [3] ---------
    [Native   ] Elapsed Time:4ms
    [Decorator] Elapsed Time:5ms
    [Dynamic  ] Elapsed Time:9ms
    [Cglib    ] Elapsed Time:26ms
    
    ==================== run test : [repeatCount=3] [runCount=50000000] [java.version=1.8.0_05] ====================
    
    --------- test : [1] ---------
    [Native   ] Elapsed Time:194ms
    [Decorator] Elapsed Time:211ms
    [Dynamic  ] Elapsed Time:538ms
    [Cglib    ] Elapsed Time:965ms
    
    --------- test : [2] ---------
    [Native   ] Elapsed Time:194ms
    [Decorator] Elapsed Time:214ms
    [Dynamic  ] Elapsed Time:503ms
    [Cglib    ] Elapsed Time:969ms
    
    --------- test : [3] ---------
    [Native   ] Elapsed Time:190ms
    [Decorator] Elapsed Time:209ms
    [Dynamic  ] Elapsed Time:495ms
    [Cglib    ] Elapsed Time:939ms

    测试结果表明:jdk8 下,延续了 JDK7 下的惊天大逆转!不过还观察另外有一个细微的变化,从绝对值来看 cglib 在 jdk8 下的表现似乎比 jdk7 还要差一点点,尽管只是一点点,但经过反复多次的执行仍然是这个趋势(注:这个趋势的结论并不严谨,只是捎带一提,如需得出结论还需进行更多样的对比实验)。

    结论:从 jdk6 到 jdk7、jdk8 ,动态代理的性能得到了显著的提升,而 cglib 的表现并未跟上,甚至可能会略微下降。传言的 cglib 比 jdk动态代理高出 10 倍的情况也许是出现在更低版本的 jdk 上吧。

    以上测试用例虽然简单,但揭示了 jdk 版本升级可能会带来一些新技术改变,会使我们以前的经验失效。放在真实业务场景下时,还需要按照实际情况进行测试后才能得出特定于场景的结论。

    总之,实践出真知,还要与时俱进地去检视更新一些以往经验。

    注:上述实验中 cglib 的版本是 3.1 。

  • 相关阅读:
    ZOJ 3332 Strange Country II
    ZOJ 3331 Process the Tasks(双塔DP)
    ZOJ 3326 An Awful Problem(模拟)
    HDU 1796 How many integers can you find(容斥原理)
    HDU 4059 The Boss on Mars(容斥原理)
    HDU 4135 Co-prime(容斥原理)
    HDU 5677 ztr loves substring(回文串加多重背包)
    CodeForces 668B Little Artem and Dance
    CodeForces 667A Pouring Rain
    Java实现 LeetCode 764 最大加号标志(暴力递推)
  • 原文地址:https://www.cnblogs.com/haiq/p/4304615.html
Copyright © 2020-2023  润新知