Java 如何强制lamda定义的新实例化

Java 如何强制lamda定义的新实例化,java,jvm,inlining,Java,Jvm,Inlining,Java规范保证将给定的lamda定义(例如,()->“Hello World”)编译/转换为一个实现类(每个定义,而不是每个“看起来”相同的出现) 有没有办法强迫java编译器/jvm生成一个新的lamda定义,而不是共享一个公共定义?我目前正在实现一个库,该库将多个函数部分编织成一个双函数,由于java规范提供的保证,该双函数会受到mega-morphic调用站点的影响(EDIT:I stand correct:java规范不保证单个共享类-但当前的参考实现会这样做): 公共双功能编织( 函

Java规范保证将给定的lamda定义(例如,
()->“Hello World”
)编译/转换为一个实现类(每个定义,而不是每个“看起来”相同的出现)

有没有办法强迫java编译器/jvm生成一个新的lamda定义,而不是共享一个公共定义?我目前正在实现一个库,该库将多个函数部分编织成一个双函数,由于java规范提供的保证,该双函数会受到mega-morphic调用站点的影响(EDIT:I stand correct:java规范不保证单个共享类-但当前的参考实现会这样做):

公共双功能编织(

函数在当前实现中,缓存生成的类(甚至是非捕获lambda表达式的实例)是指令的一个属性,它将重用在第一次执行时完成的引导结果

类中承载的bootstrap方法本身在每次调用时都会生成一个新类。因此,当您直接使用此工厂时,在当前实现下,每次调用时都会得到一个新类

public <In, Out, A> BiFunction<In, Out, Out> weave(
     Function<? super In, A> getter,
     BiConsumer<? super Out, ? super A> consumer) {

    MethodHandles.Lookup l = MethodHandles.lookup();
    try {
        MethodHandle target = l.findStatic(l.lookupClass(), "weaveLambdaBody",
            MethodType.methodType(Object.class, Function.class, BiConsumer.class,
                Object.class, Object.class));
        MethodType t = target.type().dropParameterTypes(0, 2);
        return (BiFunction<In, Out, Out>)LambdaMetafactory.metafactory(l, "apply",
            target.type().dropParameterTypes(2, 4).changeReturnType(BiFunction.class),
            t, target, t) .getTarget().invokeExact(getter, consumer);
    }
    catch(RuntimeException | Error e) {
        throw e;
    }
    catch(Throwable t) {
        throw new IllegalStateException(t);
    }
}
private static <In, Out, A> Out weaveLambdaBody(
    Function<? super In, A> getter,
    BiConsumer<? super Out, ? super A> consumer,
    In in, Out out) {

    consumer.accept(out, getter.apply(in));
    return out;
}
公共双功能编织(

FunctionIt如果没有运行时代码生成,您看起来不会有任何运气。这是一个基本的误解。规范中没有任何东西可以保证这一点。规范允许生成相同的实现类,但事实上,它明确表示相同的lambda表达式可能会导致不同的实现类。“不同lambda表达式生成的对象不必属于不同的类”表示允许不同的lambda表达式生成相同的类(这在今天的参考实现中不会发生)。下一点是“通过计算生成的每个对象不必属于同一类”(例如,捕获的局部变量可能是内联的)。“这正是您的场景。同一个lambda表达式可能会被计算到不同类的实例中(这在今天的参考实现中也不会发生)@talex每个
BiFunction
实例只捕获一个
BiConsumer
实例,总是以相同的实现结束。如果为每个
BiFunction
实例生成一个不同的类,这将是一个完美的单态行为。但我认为这不应该是必要的,因为JVM可能会内联函数通过多个调用级别将代码转换为不同的专用版本。这里的关键点是捕获的
BiConsumer
实例。也许,在这里指定
-XX:+trustFinalOnStaticFields
会有所不同。@talex是的,JIT能够内联方法调用,无论该方法的类是如何生成的。Ac实际上,静态lambda表达式是使用非常相同的
LambdaMetafactory
实例化的。谢谢,这很好(在
MethodHandles.lookup()中进行了一些调整,在单独实现枚举常量中的接口时,
不会返回正确的类)。我当前的测试套件表明,内联可以很好地使用这种方法或我问题中的方法(当有一个调用站点时)。我还有一些奇怪的性能问题,现在似乎与内联无关。感谢您的帮助!
public <In, Out, A> BiFunction<In, Out, Out> weave(
     Function<? super In, A> getter,
     BiConsumer<? super Out, ? super A> consumer) {

    MethodHandles.Lookup l = MethodHandles.lookup();
    try {
        MethodHandle target = l.findStatic(l.lookupClass(), "weaveLambdaBody",
            MethodType.methodType(Object.class, Function.class, BiConsumer.class,
                Object.class, Object.class));
        MethodType t = target.type().dropParameterTypes(0, 2);
        return (BiFunction<In, Out, Out>)LambdaMetafactory.metafactory(l, "apply",
            target.type().dropParameterTypes(2, 4).changeReturnType(BiFunction.class),
            t, target, t) .getTarget().invokeExact(getter, consumer);
    }
    catch(RuntimeException | Error e) {
        throw e;
    }
    catch(Throwable t) {
        throw new IllegalStateException(t);
    }
}
private static <In, Out, A> Out weaveLambdaBody(
    Function<? super In, A> getter,
    BiConsumer<? super Out, ? super A> consumer,
    In in, Out out) {

    consumer.accept(out, getter.apply(in));
    return out;
}