Java 反射的性能问题

2017-04-04 20:12:54 +08:00
 twogoods

最近在了解反射优化的东西,做了一个 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 快?!!! 直接调和反射差的也不多,是我的这段测试有问题吗?请教一下关于反射优化的问题,谢谢。

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

JAVA 的 benchmark 比较复杂,不但要剔除无关的 OP ,还要做好 JVM 的 JIT 预热,而且有 4 种风格迥异的 gc 算法,每种 gc 算法还有不同调优参数给你组合,还要跟 CPU 核心数和内存大小结合在一起考量,可能换个参数组合,性能可以差几倍。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/352483

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX