MethodHandle源码阅读

2022-10-12  本文已影响0人  allanYan

概述

在阅读技术文章时,经常听到MethodHandle的速度比反射快,这次准备彻底的了解下其实现原理

代码样例

       // 1.创建MethodHandles.Lookup
        MethodHandles.Lookup lookup = Calculator.getLookup();
        // 2.指定 返回值类型 和 参数类型 ,定义目标方法的MethodType
        MethodType methodType = MethodType.methodType(int.class, new Class[]{int.class, int.class});
        // 3.通过MethodHandles.Lookup指定方法定义类、方法名称以及MethodType 来查找对应的方法句柄
        MethodHandle methodHandle = lookup.findSpecial(Calculator.class, "sum", methodType, Calculator.class);
        // 4.利用方法句柄进行方法调用
        for(int i=0;i<129;i++) {
            try {
                int result = (int) methodHandle.invoke(new Calculator(), 2, 3);
                System.out.println(result);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

增加JVM参数-Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true执行该段代码,可以看到其完整的调用栈:

java.lang.ArithmeticException: / by zero
    at jvm.methodhandle.Calculator.sum(Calculator.java:7)
    at java.lang.invoke.LambdaForm$DMH006/951007336.invokeSpecial_000_LII_I(LambdaForm$DMH006:1000014)
    at java.lang.invoke.LambdaForm$MH012/868693306.invoke_000_MT(LambdaForm$MH012:1000024)
    at jvm.methodhandle.MethodHandleDemo.main(MethodHandleDemo.java:18)

lookup.findSpecial

底层调用的是getDirectMethodCommon方法

 private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
                                                   boolean checkSecurity,
                                                   boolean doRestrict, Class<?> callerClass) throws IllegalAccessException {
  DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method);
}
static DirectMethodHandle make(byte refKind, Class<?> receiver, MemberName member) {
        MethodType mtype = member.getMethodOrFieldType();
        if (!member.isStatic()) {
            if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor())
                throw new InternalError(member.toString());
            mtype = mtype.insertParameterTypes(0, receiver);
        }
        if (!member.isField()) {
            switch (refKind) {
                case REF_invokeSpecial: {
                    member = member.asSpecial();
                    LambdaForm lform = preparedLambdaForm(member);
                    return new Special(mtype, lform, member);
                }
                case REF_invokeInterface: {
                    LambdaForm lform = preparedLambdaForm(member);
                    return new Interface(mtype, lform, member, receiver);
                }
                default: {
                    LambdaForm lform = preparedLambdaForm(member);
                    return new DirectMethodHandle(mtype, lform, member);
                }
            }
        } else {
            LambdaForm lform = preparedFieldLambdaForm(member);
            if (member.isStatic()) {
                long offset = MethodHandleNatives.staticFieldOffset(member);
                Object base = MethodHandleNatives.staticFieldBase(member);
                return new StaticAccessor(mtype, lform, member, base, offset);
            } else {
                long offset = MethodHandleNatives.objectFieldOffset(member);
                assert(offset == (int)offset);
                return new Accessor(mtype, lform, member, (int)offset);
            }
        }
    }
private static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
        boolean needsInit = (which == LF_INVSTATIC_INIT);
        boolean doesAlloc = (which == LF_NEWINVSPECIAL);
        boolean needsReceiverCheck = (which == LF_INVINTERFACE);
        String linkerName, lambdaName;
        switch (which) {
        case LF_INVVIRTUAL:    linkerName = "linkToVirtual";    lambdaName = "DMH.invokeVirtual";    break;
        case LF_INVSTATIC:     linkerName = "linkToStatic";     lambdaName = "DMH.invokeStatic";     break;
        case LF_INVSTATIC_INIT:linkerName = "linkToStatic";     lambdaName = "DMH.invokeStaticInit"; break;
        case LF_INVSPECIAL:    linkerName = "linkToSpecial";    lambdaName = "DMH.invokeSpecial";    break;
        case LF_INVINTERFACE:  linkerName = "linkToInterface";  lambdaName = "DMH.invokeInterface";  break;
        case LF_NEWINVSPECIAL: linkerName = "linkToSpecial";    lambdaName = "DMH.newInvokeSpecial"; break;
        default:  throw new InternalError("which="+which);
        }
        MethodType mtypeWithArg = mtype.appendParameterTypes(MemberName.class);
        if (doesAlloc)
            mtypeWithArg = mtypeWithArg
                    .insertParameterTypes(0, Object.class)  // insert newly allocated obj
                    .changeReturnType(void.class);          // <init> returns void
        MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic);
        try {
            linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, NoSuchMethodException.class);
        } catch (ReflectiveOperationException ex) {
            throw newInternalError(ex);
        }
        final int DMH_THIS    = 0;
        final int ARG_BASE    = 1;
        final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
        int nameCursor = ARG_LIMIT;
        final int NEW_OBJ     = (doesAlloc ? nameCursor++ : -1);
        final int GET_MEMBER  = nameCursor++;
        final int CHECK_RECEIVER = (needsReceiverCheck ? nameCursor++ : -1);
        final int LINKER_CALL = nameCursor++;
        Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
        if (doesAlloc) {
            // names = { argx,y,z,... new C, init method }
            names[NEW_OBJ] = new Name(Lazy.NF_allocateInstance, names[DMH_THIS]);
            names[GET_MEMBER] = new Name(Lazy.NF_constructorMethod, names[DMH_THIS]);
        } else if (needsInit) {
            names[GET_MEMBER] = new Name(Lazy.NF_internalMemberNameEnsureInit, names[DMH_THIS]);
        } else {
            names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]);
        }
     
        Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
        if (needsReceiverCheck) {
            names[CHECK_RECEIVER] = new Name(Lazy.NF_checkReceiver, names[DMH_THIS], names[ARG_BASE]);
            outArgs[0] = names[CHECK_RECEIVER];
        }
     
        int result = LAST_RESULT;
        if (doesAlloc) {
            System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2);
            outArgs[0] = names[NEW_OBJ];
            result = NEW_OBJ;
        }
        names[LINKER_CALL] = new Name(linker, outArgs);
        lambdaName += "_" + shortenSignature(basicTypeSignature(mtype));
        LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result);
        // This is a tricky bit of code.  Don't send it through the LF interpreter.
        lform.compileToBytecode();
        return lform;
    }

可以看到,最终是通过动态字节码方式生成了JAVA类,类名前缀为java/lang/invoke/LambdaForm$MH

MethodHandle.invoke

invoke是个native方法,方法定义如下:

 public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;

对应的实现见methodHandles.cpp

static JNINativeMethod MH_methods[] = {
  // UnsupportedOperationException throwers
  {CC"invoke",                    CC"(["OBJ")"OBJ,                       FN_PTR(MH_invoke_UOE)},
  {CC"invokeExact",               CC"(["OBJ")"OBJ,                       FN_PTR(MH_invokeExact_UOE)}
};

/**
 * Throws a java/lang/UnsupportedOperationException unconditionally.
 * This is required by the specification of MethodHandle.invoke if
 * invoked directly.
 */
JVM_ENTRY(jobject, MH_invoke_UOE(JNIEnv* env, jobject mh, jobjectArray args)) {
  THROW_MSG_NULL(vmSymbols::java_lang_UnsupportedOperationException(), "MethodHandle.invoke cannot be invoked reflectively");
  return NULL;
}
JVM_END

令人惊讶的是这里直接抛出了异常;可以肯定JVM肯定是在某个环节做了特殊处理,经过查找资料,发现在类加载的link环节,有如下特殊处理:

#systemDictionary.cpp
SystemDictionary::parse_stream->InstanceKlass::link_class->InstanceKlass::link_class_impl->InstanceKlass::rewrite_class->Rewriter::rewrite->Rewriter::rewrite_bytecodes->Rewriter::scan_method->Rewriter::rewrite_member_reference->Rewriter::maybe_rewrite_invokehandle

void Rewriter::maybe_rewrite_invokehandle(address opc, int cp_index, int cache_index, bool reverse) {
  if (!reverse) {
    if ((*opc) == (u1)Bytecodes::_invokevirtual ||
        (*opc) == (u1)Bytecodes::_invokespecial) {
      if (cp_index >= _method_handle_invokers.length())  return;
      int status = _method_handle_invokers[cp_index];
   
      if (status == 0) {
        if (_pool->klass_ref_at_noresolve(cp_index) == vmSymbols::java_lang_invoke_MethodHandle() &&
       MethodHandles::is_signature_polymorphic_name(SystemDictionary::MethodHandle_klass(),
                                                         _pool->name_ref_at(cp_index))) {
          add_invokedynamic_resolved_references_entries(cp_index, cache_index);
          status = +1;
        } else {
          status = -1;
        }
        _method_handle_invokers[cp_index] = status;
      }
      if (status > 0) {
       (*opc) = (u1)Bytecodes::_invokehandle; 
      }
    }
  } else {
    if ((*opc) == (u1)Bytecodes::_invokehandle) {
      (*opc) = (u1)Bytecodes::_invokevirtual;
    }
  }
}

可以看到此处会对带有PolymorphicSignature注解的MethodHandle方法进行特殊处理,从_invokevirtual指令重写为_invokehandle;

invokehandle指令

invokehandle指令的实现,可以参考bytecodeInterpreter.cpp:

CASE(_invokehandle): {

        if (!EnableInvokeDynamic) {
          ShouldNotReachHere();
        }

        u2 index = Bytes::get_native_u2(pc+1);
        ConstantPoolCacheEntry* cache = cp->entry_at(index);

        if (! cache->is_resolved((Bytecodes::Code) opcode)) {
          CALL_VM(InterpreterRuntime::resolve_invokehandle(THREAD),
                  handle_exception);
          cache = cp->entry_at(index);
        }

        Method* method = cache->f1_as_method();
        if (VerifyOops) method->verify();

        if (cache->has_appendix()) {
          ConstantPool* constants = METHOD->constants();
          SET_STACK_OBJECT(cache->appendix_if_resolved(constants), 0);
          MORE_STACK(1);
        }

        istate->set_msg(call_method);
        istate->set_callee(method);
        istate->set_callee_entry_point(method->from_interpreted_entry());
        istate->set_bcp_advance(3);

        BI_PROFILE_UPDATE_FINALCALL();

        UPDATE_PC_AND_RETURN(0); // I'll be back...
      }

InterpreterRuntime::resolve_invokehandle方法代码位于interpreterRuntime.cpp文件:

InterpreterRuntime::resolve_invokehandle->LinkResolver::resolve_invoke->LinkResolver::resolve_invokehandle->LinkResolver::resolve_handle_call->LinkResolver::lookup_polymorphic_method->SystemDictionary::find_method_handle_invoker

methodHandle SystemDictionary::find_method_handle_invoker(Symbol* name,
                                                          Symbol* signature,
                                                          KlassHandle accessing_klass,
                                                          Handle *appendix_result,
                                                          Handle *method_type_result,
                                                          TRAPS) {
  methodHandle empty;
  Handle method_type =
    SystemDictionary::find_method_handle_type(signature, accessing_klass, CHECK_(empty));

  KlassHandle  mh_klass = SystemDictionary::MethodHandle_klass();
  int ref_kind = JVM_REF_invokeVirtual;
  Handle name_str = StringTable::intern(name, CHECK_(empty));
  objArrayHandle appendix_box = oopFactory::new_objArray(SystemDictionary::Object_klass(), 1, CHECK_(empty));
 
  // call java.lang.invoke.MethodHandleNatives::linkMethod(... String, MethodType) -> MemberName
  JavaCallArguments args;
  args.push_oop(accessing_klass()->java_mirror());
  args.push_int(ref_kind);
  args.push_oop(mh_klass()->java_mirror());
  args.push_oop(name_str());
  args.push_oop(method_type());
  args.push_oop(appendix_box());
  JavaValue result(T_OBJECT);
  JavaCalls::call_static(&result,
                         SystemDictionary::MethodHandleNatives_klass(),
                         vmSymbols::linkMethod_name(),
                         vmSymbols::linkMethod_signature(),
                         &args, CHECK_(empty));
  Handle mname(THREAD, (oop) result.get_jobject());
  (*method_type_result) = method_type;
  return unpack_method_and_appendix(mname, accessing_klass, appendix_box, appendix_result, THREAD);
}

可以看到最终调用的是 java.lang.invoke.MethodHandleNatives::linkMethod方法
那么MethodHandleNatives返回的是什么内容呢?


截屏2022-10-12 17.31.55.png

可以看到此处返回的MemberName和前面调用栈是一致的;

LambdaForm$MH012

package java.lang.invoke;

import java.lang.invoke.LambdaForm.Compiled;
import java.lang.invoke.LambdaForm.Hidden;

final class LambdaForm$MH012 {
    @Hidden
    @Compiled
    @ForceInline
    static int invoke_000_MT(Object var0, Object var1, int var2, int var3, Object var4) {
        Object var5 = Invokers.checkGenericType(var0, var4);
        Invokers.checkCustomized(var5);
        MethodHandle var6;
        return (var6 = (MethodHandle)var5).invokeBasic(var1, var2, var3);
    }

    static void dummy() {
        String var10000 = "invoke_000_MT=Lambda(a0:L,a1:L,a2:I,a3:I,a4:L)=>{\n    t5:L=Invokers.checkGenericType(a0:L,a4:L);\n    t6:V=Invokers.checkCustomized(t5:L);\n    t7:I=MethodHandle.invokeBasic(t5:L,a1:L,a2:I,a3:I);t7:I}:LLIIL_I/null";
    }
}

此处的Invokers.checkCustomized要特殊说明下,当MethodHandle调用次数达到一定阈值时(默认127,可以通过-Djava.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD进行调整,但必须位于[-1...127]范围内),会定制一个LambdaForm;

LambdaForm$DMH006

package java.lang.invoke;

import java.lang.invoke.LambdaForm.Compiled;
import java.lang.invoke.LambdaForm.Hidden;

final class LambdaForm$DMH006 {
    @Hidden
    @Compiled
    @ForceInline
    static int invokeSpecial_000_LII_I(Object var0, Object var1, int var2, int var3) {
        Object var4 = DirectMethodHandle.internalMemberName(var0);
        return MethodHandle.linkToSpecial(var1, var2, var3, (MemberName)var4);
    }

    static void dummy() {
        String var10000 = "DMH.invokeSpecial_000_LII_I=Lambda(a0:L,a1:L,a2:I,a3:I)=>{\n    t4:L=DirectMethodHandle.internalMemberName(a0:L);\n    t5:I=MethodHandle.linkToSpecial(a1:L,a2:I,a3:I,t4:L);t5:I}:LLII_I/null";
    }
}

可以看到最终调用的是MethodHandle.linkToSpecial方法

MethodHandle.linkToSpecial

查看methodHandles.cpp文件,发现是通过MethodHandles::generate_adapters方法实现的:

MethodHandles::generate_adapters->MethodHandlesAdapterGenerator::generate->MethodHandles::generate_method_handle_interpreter_entry->MethodHandles::generate_method_handle_dispatch

void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm,
                                                    vmIntrinsics::ID iid,
                                                    Register receiver_reg,
                                                    Register member_reg,
                                                    bool for_compiler_entry) {
  assert(is_signature_polymorphic(iid), "expected invoke iid");
  Register rbx_method = rbx;   
 
  Register temp1 = rscratch1;
  Register temp2 = rscratch2;
  Register temp3 = rax;
 
  if (iid == vmIntrinsics::_invokeBasic) {
    // indirect through MH.form.vmentry.vmtarget
    jump_to_lambda_form(_masm, receiver_reg, rbx_method, temp1, for_compiler_entry);

  } else {
    // The method is a member invoker used by direct method handles.
    Address member_clazz(    member_reg, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes()));
    Address member_vmindex(  member_reg, NONZERO(java_lang_invoke_MemberName::vmindex_offset_in_bytes()));
    Address member_vmtarget( member_reg, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes()));

    Register temp1_recv_klass = temp1;
    if (iid != vmIntrinsics::_linkToStatic) {
      __ verify_oop(receiver_reg);
      if (iid == vmIntrinsics::_linkToSpecial) {
        // Don't actually load the klass; just null-check the receiver.
        __ null_check(receiver_reg);
      } else {
        // load receiver klass itself
        __ null_check(receiver_reg, oopDesc::klass_offset_in_bytes());
        __ load_klass(temp1_recv_klass, receiver_reg);
        __ verify_klass_ptr(temp1_recv_klass);
      }
     
      // The receiver for the MemberName must be in receiver_reg.
      // Check the receiver against the MemberName.clazz
      if (VerifyMethodHandles && iid == vmIntrinsics::_linkToSpecial) {
        __ load_klass(temp1_recv_klass, receiver_reg);
        __ verify_klass_ptr(temp1_recv_klass);
      }
    }
    // Live registers at this point:
    //  member_reg - MemberName that was the trailing argument
    //  temp1_recv_klass - klass of stacked receiver, if needed
    //  rsi/r13 - interpreter linkage (if interpreted)
    //  rcx, rdx, rsi, rdi, r8 - compiler arguments (if compiled)

    Label L_incompatible_class_change_error;
    switch (iid) {
    case vmIntrinsics::_linkToSpecial:
      __ movptr(rbx_method, member_vmtarget);
      break;

    case vmIntrinsics::_linkToStatic:
      __ movptr(rbx_method, member_vmtarget);
      break;

    case vmIntrinsics::_linkToVirtual:
    {
      // pick out the vtable index from the MemberName, and then we can discard it:
      Register temp2_index = temp2;
      __ movptr(temp2_index, member_vmindex);

      // Note:  The verifier invariants allow us to ignore MemberName.clazz and vmtarget
      // at this point.  And VerifyMethodHandles has already checked clazz, if needed.

      // get target Method* & entry point
      __ lookup_virtual_method(temp1_recv_klass, temp2_index, rbx_method);
      break;
    }

    case vmIntrinsics::_linkToInterface:
    {
      // same as TemplateTable::invokeinterface
      // (minus the CP setup and profiling, with different argument motion)

      Register temp3_intf = temp3;
      __ load_heap_oop(temp3_intf, member_clazz);
      load_klass_from_Class(_masm, temp3_intf);
      __ verify_klass_ptr(temp3_intf);

      Register rbx_index = rbx_method;
      __ movptr(rbx_index, member_vmindex);

      // given intf, index, and recv klass, dispatch to the implementation method
      __ lookup_interface_method(temp1_recv_klass, temp3_intf,
                                 // note: next two args must be the same:
                                 rbx_index, rbx_method,
                                 temp2,
                                 L_incompatible_class_change_error);
      break;
    }

    default:
      fatal(err_msg_res("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid)));
      break;
    }

    // After figuring out which concrete method to call, jump into it.
    // Note that this works in the interpreter with no data motion.
    // But the compiled version will require that rcx_recv be shifted out.
    __ verify_method_ptr(rbx_method);
    jump_from_method_handle(_masm, rbx_method, temp1, for_compiler_entry);

    if (iid == vmIntrinsics::_linkToInterface) {
      __ bind(L_incompatible_class_change_error);
      __ jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry()));
    }
  }
}

可以看到此处会直接调用Calculator.sum方法;

上一篇下一篇

猜你喜欢

热点阅读