在静态块中初始化final和not final静态字段

时间:2019-07-02 21:00:39

标签: java methodhandle

Here我发现以下代码显示了MethodHandles和Reflection的性能差异:

@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class MHOpto {

    private int value = 42;

    private static final Field static_reflective;
    private static final MethodHandle static_unreflect;
    private static final MethodHandle static_mh;

    private static Field reflective;
    private static MethodHandle unreflect;
    private static MethodHandle mh;

    // We would normally use @Setup, but we need to initialize "static final" fields here...
    static {
        try {
            reflective = MHOpto.class.getDeclaredField("value");
            unreflect = MethodHandles.lookup().unreflectGetter(reflective);
            mh = MethodHandles.lookup().findGetter(MHOpto.class, "value", int.class);
            static_reflective = reflective;
            static_unreflect = unreflect; //LINE X!!!
            static_mh = mh;
        } catch (IllegalAccessException | NoSuchFieldException e) {
            throw new IllegalStateException(e);
        }
    }

    @Benchmark
    public int plain() {
        return value;
    }

    @Benchmark
    public int dynamic_reflect() throws InvocationTargetException, IllegalAccessException {
        return (int) reflective.get(this);
    }

    @Benchmark
    public int dynamic_unreflect_invoke() throws Throwable {
        return (int) unreflect.invoke(this);
    }

    @Benchmark
    public int dynamic_unreflect_invokeExact() throws Throwable {
        return (int) unreflect.invokeExact(this);
    }

    @Benchmark
    public int dynamic_mh_invoke() throws Throwable {
        return (int) mh.invoke(this);
    }

    @Benchmark
    public int dynamic_mh_invokeExact() throws Throwable {
        return (int) mh.invokeExact(this);
    }

    @Benchmark
    public int static_reflect() throws InvocationTargetException, IllegalAccessException {
        return (int) static_reflective.get(this);
    }

    @Benchmark
    public int static_unreflect_invoke() throws Throwable {
        return (int) static_unreflect.invoke(this);
    }

    @Benchmark
    public int static_unreflect_invokeExact() throws Throwable {
        return (int) static_unreflect.invokeExact(this);
    }

    @Benchmark
    public int static_mh_invoke() throws Throwable {
        return (int) static_mh.invoke(this);
    }

    @Benchmark
    public int static_mh_invokeExact() throws Throwable {
        return (int) static_mh.invokeExact(this);
    }

}

这些是结果:

Benchmark                             Mode  Cnt  Score   Error  Units
MHOpto.dynamic_mh_invoke              avgt   25  4.393 ± 0.003  ns/op
MHOpto.dynamic_mh_invokeExact         avgt   25  4.394 ± 0.007  ns/op
MHOpto.dynamic_reflect                avgt   25  5.230 ± 0.020  ns/op
MHOpto.dynamic_unreflect_invoke       avgt   25  4.404 ± 0.023  ns/op
MHOpto.dynamic_unreflect_invokeExact  avgt   25  4.397 ± 0.014  ns/op
MHOpto.plain                          avgt   25  1.858 ± 0.002  ns/op
MHOpto.static_mh_invoke               avgt   25  1.862 ± 0.015  ns/op
MHOpto.static_mh_invokeExact          avgt   25  1.859 ± 0.002  ns/op
MHOpto.static_reflect                 avgt   25  4.274 ± 0.011  ns/op
MHOpto.static_unreflect_invoke        avgt   25  1.859 ± 0.002  ns/op
MHOpto.static_unreflect_invokeExact   avgt   25  1.858 ± 0.002  ns/op

我不明白的是这一行代码:

static_unreflect = unreflect;

static_unreflect(最终)是否等于unreflect(不是最终值)?那么为什么他们在表现上会表现出不同的结果?谁能解释?

1 个答案:

答案 0 :(得分:2)

JIT仅将MethodHandle的t[[0], [0]] += 10变体视为常量,请参见例如ciField

tensor([[10,  1],
        [ 1,  0]])

仅内联通过MethodHandles进行的调用是内联的,请参见CallGenerator::for_method_handle_inline,在其中进行多次检查以确保接收方是常量的,例如:

static final

通过这种区别,可以内联// Is this field a constant? // // Clarification: A field is considered constant if: // 1. The field is both static and final // 2. The field is not one of the special static/final // non-constant fields. These are java.lang.System.in // and java.lang.System.out. Abomination. // // A field is also considered constant if // - it is marked @Stable and is non-null (or non-zero, if a primitive) or // - it is trusted or // - it is the target field of a CallSite object. // // See ciField::initialize_from() for more details. // // A user should also check the field value (constant_value().is_valid()), since // constant fields of non-initialized classes don't have values yet. bool is_constant() const { return _is_constant; } MethodHandle的调用,因此其速度与普通情况一样快。

如果打印出内联信息,则也可以看到此信息。例如您可以添加以下内容:

Node* receiver = kit.argument(0);
if (receiver->Opcode() == Op_ConP) {
  ...
} else {
  print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
                         "receiver not constant");
}

参考基准方法。

在静态情况下,您会看到内联的呼叫:

static final

我们一直在进行@Fork(jvmArgsAppend="-Xlog:inlining*=trace:inlining-%p-static_mh_invokeExact.txt") 调用的所有过程(但是重要的是,我们看到的是 @ 17 org.sample.MyBenchmark::static_mh_invokeExact (8 bytes) force inline by CompileCommand @ 4 java.lang.invoke.LambdaForm$MH/0x00000008000f0040::invokeExact_MT (23 bytes) force inline by annotation @ 10 java.lang.invoke.Invokers::checkExactType (17 bytes) force inline by annotation @ 1 java.lang.invoke.MethodHandle::type (5 bytes) @ 14 java.lang.invoke.Invokers::checkCustomized (23 bytes) force inline by annotation @ 1 java.lang.invoke.MethodHandleImpl::isCompileConstant (2 bytes) @ 19 java.lang.invoke.LambdaForm$MH/0x00000008000f0440::getInt (34 bytes) force inline by annotation @ 7 java.lang.invoke.DirectMethodHandle::fieldOffset (9 bytes) force inline by annotation @ 12 java.lang.invoke.DirectMethodHandle::checkBase (5 bytes) force inline by annotation @ 1 java.util.Objects::requireNonNull (14 bytes) @ 8 java.lang.NullPointerException::<init> (5 bytes) don't inline Throwable constructors @ 30 jdk.internal.misc.Unsafe::getInt (0 bytes) intrinsic 而不是Unsafe::getInt)。

在动态情况下,您最多会看到:

@ 19   java.lang.invoke.LambdaForm$MH/0x00000008000f0440::getInt

即在这种情况下,仍然会通过invokeBasic stub进行间接调用,因为“接收方不是常量”。