美文网首页
MethodHandle源码阅读

MethodHandle源码阅读

作者: allanYan | 来源:发表于2022-10-12 17:14 被阅读0次

    概述

    在阅读技术文章时,经常听到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方法;

    相关文章

      网友评论

          本文标题:MethodHandle源码阅读

          本文链接:https://www.haomeiwen.com/subject/tfhzartx.html