V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
twogoods
V2EX  ›  程序员

Java 反射的性能问题

  •  
  •   twogoods · 2017-04-04 20:12:54 +08:00 · 3252 次点击
    这是一个创建于 2789 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在了解反射优化的东西,做了一个 cglib,java 的反射 api 和直接调用的性能比较

    public class SampleBean {
        public String echo(String name) {
            return name;
        }
    }
    
    测试代码:
    
            FastClass fastClass = FastClass.create(SampleBean.class);
            FastMethod fastMethod = fastClass.getMethod(SampleBean.class.getMethod("echo", String.class));
            SampleBean myBean = new SampleBean();
            long start = System.currentTimeMillis();
            for (int i = 0; i < 1000000; i++) {
                fastMethod.invoke(myBean, new Object[]{"haha"+i});
                //fastMethod.invoke(myBean, new Object[]{"haha"});
            }
            System.out.println("fastmethod:" + (System.currentTimeMillis() - start));
            
            start = System.currentTimeMillis();
            Method m = SampleBean.class.getMethod("echo", String.class);
            for (int i = 0; i < 1000000; i++) {
                m.invoke(myBean, "haha"+i);
                //m.invoke(myBean, "haha");
            }
            System.out.println("reflect:"+(System.currentTimeMillis() - start));
            
            start = System.currentTimeMillis();
            for (int i = 0; i < 1000000; i++) {
                myBean.echo("haha"+i);
                //myBean.echo("haha");
            }
            System.out.println("normal:"+(System.currentTimeMillis() - start));
    

    输出: fastmethod:196 reflect:139 normal:72 上面一段代码如果跑被注释掉的那一行时间会更短: fastmethod:38 reflect:33 normal:25 个人猜测,百万次调用参数不变,是不是被优化了?

    public class SampleBean {
        public String echo(String name, int age) {
            //改一下,方法稍微复杂点
            List<String> names = new ArrayList<>(100);
            for (int i = 0; i < 100; i++) {
                names.add(name+i);
            }
            List<Integer> ages = new ArrayList<>(100);
            for (int i = 0; i < 100; i++) {
                ages.add(age+i);
            }
            return name+"--"+age;
        }
    }
    
    测试代码:
    
    FastClass fastClass = FastClass.create(SampleBean.class);
            FastMethod fastMethod = fastClass.getMethod(SampleBean.class.getMethod("echo", String.class, int.class));
            SampleBean myBean = new SampleBean();
            myBean.setValue("Hello cglib!");
            long start = System.currentTimeMillis();
            for (int i = 0; i < 1000000; i++) {
                fastMethod.invoke(myBean, new Object[]{"haha"+i, 12});
            }
            System.out.println("fastmethod:" + (System.currentTimeMillis() - start));
            start = System.currentTimeMillis();
            Method m = SampleBean.class.getMethod("echo", String.class, int.class);
            for (int i = 0; i < 1000000; i++) {
                m.invoke(myBean, "haha"+i, 12);
            }
            System.out.println("reflect:" + (System.currentTimeMillis() - start));
            start = System.currentTimeMillis();
            for (int i = 0; i < 1000000; i++) {
                myBean.echo("haha"+i, 12);
            }
            System.out.println("normal:" + (System.currentTimeMillis() - start));
    

    这段代码输出: fastmethod:5533 reflect:4779 normal:4691 结果是 cglib 不如 java 自己的反射 api 快?!!! 直接调和反射差的也不多,是我的这段测试有问题吗?请教一下关于反射优化的问题,谢谢。

    6 条回复    2017-04-04 23:39:42 +08:00
    sagaxu
        1
    sagaxu  
       2017-04-04 20:18:20 +08:00
    你可以单独把"haha"+i 拿出来测一下性能, int 转 String ,再做一个 String 的拼接,是个比较重的操作
    sagaxu
        2
    sagaxu  
       2017-04-04 20:19:46 +08:00
    而且你的测试代码都没有经过 JIT 预热,预热之后再测会更准确
    misaka19000
        3
    misaka19000  
       2017-04-04 20:20:01 +08:00 via Android
    javap 看一下有什么区别
    twogoods
        4
    twogoods  
    OP
       2017-04-04 23:23:18 +08:00 via Android
    @sagaxu 拼字符串的过程大家都有应该关系不大吧,我是单纯好奇 JDK 自己的实现已经比 cglib 都好了
    unique
        5
    unique  
       2017-04-04 23:31:28 +08:00   ❤️ 1
    这里有更详细的测试,或许可以帮到你 https://github.com/neoremind/dynamic-proxy
    sagaxu
        6
    sagaxu  
       2017-04-04 23:39:42 +08:00 via Android
    @twogoods 关系当然很大,比如 15:20:150 比例悬殊,但是大家都加个 1000 ,变成 1015:1020:1150 ,可以认为相差不大了。

    JAVA 的 benchmark 比较复杂,不但要剔除无关的 OP ,还要做好 JVM 的 JIT 预热,而且有 4 种风格迥异的 gc 算法,每种 gc 算法还有不同调优参数给你组合,还要跟 CPU 核心数和内存大小结合在一起考量,可能换个参数组合,性能可以差几倍。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   938 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 21:18 · PVG 05:18 · LAX 13:18 · JFK 16:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.