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