You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何LambdaMetafactory性能比Reflection差?JMH测试结果异常

LambdaMetafactory性能不如反射?JMH测试结果与预期不符的问题分析

我从《Faster alternatives to Java's reflection》一文了解到,结合MethodHandle使用LambdaMetafactory的调用性能接近直接调用,但在我的JMH测试中,LambdaMetafactory的性能反而比Reflection更差,与预期情况不符,不清楚问题出在哪里。

JMH测试结果(AverageTime模式)

Benchmark                                      Mode  Cnt     Score     Error  Units
MhExceptionBenchMark.MhExceptioTest.directNew  avgt   10   248.059 ±  52.375  ns/op
MhExceptionBenchMark.MhExceptioTest.mhLamda    avgt   10  2435.962 ±  45.767  ns/op
MhExceptionBenchMark.MhExceptioTest.mhNoLamda  avgt   10  2573.739 ± 688.203  ns/op
MhExceptionBenchMark.MhExceptioTest.reflet     avgt   10  2344.597 ± 237.946  ns/op

目标类定义

ValidException类

public class ValidException extends AbstractException {

    private static final long serialVersionUID = 1L;

    private static final String DEFAULT_VALID_ERRCODE = "test";

    public ValidException() {
        super(DEFAULT_VALID_ERRCODE);
    }

    public ValidException(String errMessage) {
        super(DEFAULT_VALID_ERRCODE, errMessage);
    }
}

AbstractException父类

public abstract class AbstractException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private String errMessage;

    public AbstractException(String errMessage) {
        super(errMessage);
    }
}

JMH测试代码

@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 3)
@Measurement(iterations = 10, time = 5)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Threads(10)
public class MhExceptioTest {

    @State(Scope.Benchmark)
    public static class Ex {
        ValidException validException;

        @Setup(Level.Trial)
        public void prepare() {
            validException = new ValidException();
        }
    }

    @State(Scope.Benchmark)
    public static class MhNoLambda{
        MethodHandle methodHandle;

        @Setup(Level.Trial)
        public void prepare() throws NoSuchMethodException, IllegalAccessException {
            MethodType methodType = MethodType.methodType(void.class, String.class);
            methodHandle = MethodHandles.publicLookup().findConstructor(ValidException.class, methodType);
        }
    }

    @State(Scope.Benchmark)
    public static class MhLambda{
        Function<String,AbstractException> function;

        @Setup(Level.Trial)
        public void prepare() throws Throwable {
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodType methodType = MethodType.methodType(void.class, String.class);
            MethodHandle methodHandle = lookup.findConstructor(ValidException.class, methodType);
            CallSite site = LambdaMetafactory.metafactory(
                    lookup,
                    "apply",
                    MethodType.methodType(Function.class),
                    methodHandle.type().generic(),
                    methodHandle,
                    methodHandle.type());
            function = (Function<String, AbstractException>)site.getTarget().invokeExact();
        }
    }

    @State(Scope.Benchmark)
    public static class Constr{
        Constructor<ValidException> constructor;

        @Setup(Level.Trial)
        public void prepare() throws NoSuchMethodException {
            constructor = ValidException.class.getConstructor(String.class);
        }
    }


    @Benchmark
    public <T extends AbstractException> AbstractException directNew(Ex ex){
        ValidException validException = ex.validException;
        validException.setErrMessage("test");
        return validException;
    }

    @Benchmark
    public <T extends AbstractException> AbstractException mhNoLamda(MhNoLambda mhNoLambda) throws Throwable {
        return (AbstractException) mhNoLambda.methodHandle.invoke("test");
    }

    @Benchmark
    public <T extends AbstractException> AbstractException reflet(Constr constr) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        ValidException validException = constr.constructor.newInstance("test");
        return validException;
    }

    @Benchmark
    public <T extends AbstractException> AbstractException mhLamda(MhLambda mhLambda){
        return mhLambda.function.apply("test");
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(MhExceptioTest.class.getSimpleName())
                .result("./result-mh-ex.json")
                .resultFormat(ResultFormatType.JSON)
                .build();
        new Runner(options).run();
    }
}

问题原因分析

1. directNew基准测试逻辑无效

directNew方法并没有创建新的ValidException实例,只是复用了@Setup阶段初始化好的单个实例,仅修改其errMessage属性。而其他三个测试方法(mhLamda、mhNoLamda、reflet)都是每次调用都创建新实例,两者核心逻辑完全不同,因此directNew的性能结果不具备参考性,不能用来对比动态创建实例的性能。

2. MethodHandle调用未使用最优方式

mhNoLamda中使用methodHandle.invoke("test"),而invoke方法会进行类型适配和自动装箱/拆箱,带来额外开销。应该使用invokeExact来避免这些开销,修改为:

return (AbstractException) mhNoLambda.methodHandle.invokeExact("test");

3. LambdaMetafactory的潜在优化点

虽然你的LambdaMetafactory使用逻辑大体正确,但可以调整参数以确保生成的Lambda实现更高效:

  • 显式指定samMethodType参数:Function接口的apply方法泛型擦除后的类型是MethodType.methodType(Object.class, Object.class),你当前使用的methodHandle.type().generic()等价于这个类型,但显式指定可避免潜在类型匹配问题:
    MethodType samType = MethodType.methodType(Object.class, Object.class);
    CallSite site = LambdaMetafactory.metafactory(
            lookup,
            "apply",
            MethodType.methodType(Function.class),
            samType,
            methodHandle,
            methodHandle.type());
    
  • 增加预热迭代次数:当前@Warmup(iterations = 3)可能不足以让JVM完成Lambda的优化,尝试将预热次数提升至5次,给JVM足够时间生成优化后的字节码。

4. 现代JVM对反射的优化

现代JVM(如OpenJDK 8+)对反射调用有显著优化,当反射方法被多次调用后,JVM会生成动态字节码替代原生反射调用,缩小了与MethodHandle的性能差距。但LambdaMetafactory生成的Lambda理论上应该能超过反射,需修正测试问题后重新验证。

5. 线程数带来的干扰

你使用了@Threads(10),多线程环境下对象创建的内存分配竞争可能影响性能测试结果。建议先使用单线程(@Threads(1))测试,排除线程竞争干扰后再观察性能差异。

内容的提问来源于stack exchange,提问作者benym

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 00:40:22