Java對象創(chuàng)建的本質(zhì)就是按照對象的大小分配一塊內(nèi)存,然后完成屬性的初始化。對象創(chuàng)建完了,接著干啥了?調(diào)用Java方法完成特定功能。這就是我們接下來探討的主題,Java方法調(diào)用是怎么實現(xiàn)的。
一、Main方法
main方法是Java應(yīng)用啟動執(zhí)行的入口方法,這個方法是怎么執(zhí)行的了?,關(guān)鍵代碼在OpenJDK jdk/src/share/bin/java.c中的int JNICALL JavaMain(void * _args)方法,如下圖:

即main方法是通過JNI的CallStaticVoidMethod方法執(zhí)行的。
二、JNI 方法調(diào)用
1、API定義
JNI的方法調(diào)用的API,分為三種,總結(jié)如下:
- NativeType Call<type>Method:執(zhí)行非靜態(tài)方法調(diào)用,如果子類覆寫父類方法,則調(diào)用子類覆寫后的方法。
- NativeType CallNonvirtual<type>Method:Call<type>Method的擴展版,如果子類覆寫父類方法,可以根據(jù)jmethedId調(diào)用子類覆寫后的方法或者調(diào)用父類原來的方法,前者jmethodId從子類jclass中獲取,后者jmethodId從父類jclass中獲取。
- NativeType CallStatic<type>Method:執(zhí)行靜態(tài)方法調(diào)用。
三者根據(jù)傳遞參數(shù)的方式的不同,又有三個“重載”方法,以Call<type>Method為例:
- Call<type>Method:按照Java方法中定義的參數(shù)類型和順序依次傳遞參數(shù)即可,注意JNI中不支持基本類型的自動裝箱拆箱,如果方法參數(shù)是包裝類,則需要調(diào)用包裝類的構(gòu)造方法構(gòu)造基本包裝類實例。
- Call<type>MethodA:按照Java方法中定義的參數(shù)類型和順序?qū)⑺袇?shù)放入一個jvalue數(shù)組中,然后傳入該數(shù)組的指針即可,jvalue是一個union類型,可以支持所有的參數(shù)類型。
Call<type>MethodV:按照Java方法中定義的參數(shù)類型和順序?qū)⑺袇?shù)放入一個va_list類中,該類表示一個參數(shù)列表。
通過宏的方式實現(xiàn)不同NativeType和type下的方法定義,以Call<type>Method為例,如下圖:

所有方法調(diào)用的API最終調(diào)用的都是jni.cpp中jni_invoke_static和jni_invoke_nonstatic方法,這兩個方法的調(diào)用如下圖:

其中jni_NewObject三個方法是通過jni_invoke_nonstatic調(diào)用類構(gòu)造方法

2、 jni_invoke_static
static void jni_invoke_static(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) {
//resolve_jmethod_id將jmethodID轉(zhuǎn)換成Method*
methodHandle method(THREAD, Method::resolve_jmethod_id(method_id));
// Create object to hold arguments for the JavaCall, and associate it with
// the jni parser
ResourceMark rm(THREAD);
//獲取方法參數(shù)個數(shù)
int number_of_parameters = method->size_of_parameters();
//初始化java_args
JavaCallArguments java_args(number_of_parameters);
args->set_java_argument_object(&java_args);
//校驗?zāi)繕朔椒殪o態(tài)方法
assert(method->is_static(), "method should be static");
//fingerprint返回目標方法的fingerprint,是一長串數(shù)字,包含方法的參數(shù)類型,返回值類型等信息
/解析方法參數(shù)到JavaCallArguments中
args->iterate( Fingerprinter(method).fingerprint() );
// 設(shè)置返回結(jié)果的結(jié)果類型
result->set_type(args->get_ret_type());
//執(zhí)行方法調(diào)用
JavaCalls::call(result, method, &java_args, CHECK);
// 如果結(jié)果類型是對象或者數(shù)組類型,則需要將其轉(zhuǎn)換成本地引用
if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) {
result->set_jobject(JNIHandles::make_local(env, (oop) result->get_jobject()));
}
}
enum JNICallType {
JNI_STATIC, //靜態(tài)方法
JNI_VIRTUAL, //虛方法
JNI_NONVIRTUAL //非虛方法
};
inline static Method* resolve_jmethod_id(jmethodID mid) {
assert(mid != NULL, "JNI method id should not be null");
//據(jù)此可知,jmethodID實際是Method的指針的指針
return *((Method**)mid);
}
3、jni_invoke_nonstatic
static void jni_invoke_nonstatic(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) {
//校驗調(diào)用實例方法關(guān)聯(lián)的對象receiver不能為空
oop recv = JNIHandles::resolve(receiver);
if (recv == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
Handle h_recv(THREAD, recv);
int number_of_parameters;
Method* selected_method;
{
//將jmethodID轉(zhuǎn)換成Method*
Method* m = Method::resolve_jmethod_id(method_id);
//獲取方法個數(shù)
number_of_parameters = m->size_of_parameters();
//獲取此方法所屬的類Klass
Klass* holder = m->method_holder();
if (call_type != JNI_VIRTUAL) {
//如果是非虛方法調(diào)用,即CallNonvirtual<type>Method,則使用指定的方法
//此時jmethodID從父類Klass獲取的則使用父類的實現(xiàn),如果使用子類Klass的實現(xiàn)則使用子類的實現(xiàn)
selected_method = m;
}
//虛方法調(diào)用,即Call<type>Method,itable即接口方法表
else if (!m->has_itable_index()) {
// non-interface call -- for that little speed boost, don't handlize
// 非接口方法調(diào)用
debug_only(No_Safepoint_Verifier nosafepoint;)
//校驗該方法在虛方法表的索引是否有效,如果目標類已經(jīng)完成鏈接和初始化則valid_vtable_index()方法返回true
assert(m->valid_vtable_index(), "no valid vtable index");
//獲取虛方法表中的索引,注意同一個方法,無論從子類Klass獲取還是從父類Klass獲取,其vtbl_index都是一樣的
int vtbl_index = m->vtable_index();
//如果vtbl_index不等于nonvirtual_vtable_index,nonvirtual_vtable_index表示該方法不需要通過vtable分發(fā),即父類定義的final方法
if (vtbl_index != Method::nonvirtual_vtable_index) {
//獲取receiver對應(yīng)的Klass
Klass* k = h_recv->klass();
InstanceKlass *ik = (InstanceKlass*)k;
//獲取目標Klass在指定虛方法表索引處的虛方法實現(xiàn),
//如receiver實際是子類實例,jmethodID無論從父類Klass還是子類Klass獲取的,實際調(diào)用的都是子類的實現(xiàn)
selected_method = ik->method_at_vtable(vtbl_index);
} else {
//final方法
selected_method = m;
}
} else {
//接口方法
KlassHandle h_holder(THREAD, holder);
//獲取接口方法表中的索引,無論jmethodID從接口類Klass還是實現(xiàn)類Klass獲取的,其itbl_index都是一樣的
int itbl_index = m->itable_index();
Klass* k = h_recv->klass();
//獲取接口方法,使用receiver實例實際的類的接口實現(xiàn)
selected_method = InstanceKlass::cast(k)->method_at_itable(h_holder(), itbl_index, CHECK);
}
}
methodHandle method(THREAD, selected_method);
/
ResourceMark rm(THREAD);
//初始化JavaCallArguments
JavaCallArguments java_args(number_of_parameters);
args->set_java_argument_object(&java_args);
//校驗方法不是靜態(tài)方法
assert(!method->is_static(), "method should not be static");
//設(shè)置接受方法調(diào)用的對象實例
args->push_receiver(h_recv); // Push jobject handle
//解析方法參數(shù)
args->iterate( Fingerprinter(method).fingerprint() );
//設(shè)置方法返回類型
result->set_type(args->get_ret_type());
//調(diào)用方法
JavaCalls::call(result, method, &java_args, CHECK);
//處理結(jié)果返回值
if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) {
result->set_jobject(JNIHandles::make_local(env, (oop) result->get_jobject()));
}
}
從上述實現(xiàn)可知,jni_invoke_nonstatic比jni_invoke_static就是多了確認實際調(diào)用方法的邏輯。CallNonvirtual<type>Method時和CallStatic<type>Method實際邏輯是一樣的,都是執(zhí)行直接執(zhí)行傳入的jmethodID對應(yīng)的Java方法,而Call<type>Method需要根據(jù)傳入的jmethodID確認目標方法的虛方法表索引或者接口方法表索引,然后根據(jù)索引獲取目標實例對象所屬的類的實現(xiàn)方法。itable和vtable可參考《Hotspot Klass模型——Java類內(nèi)存表示機制》。
三、方法調(diào)用字節(jié)碼指令
1、指令定義
main方法通過JNI的方法調(diào)用開始執(zhí)行后,如果調(diào)用其他的Java方法就必須通過方法調(diào)用的字節(jié)碼指令,JNI的方法調(diào)用僅限于本地方法實現(xiàn)使用。執(zhí)行方法調(diào)用的字節(jié)碼指令總共有5個,概述如下,詳情可以參考《Java虛擬機規(guī)范》:
- invokedynamic:調(diào)用動態(tài)方法,動態(tài)方法是指最終被調(diào)用的方法可以在運行期由程序動態(tài)指定,從JDK7引入,但是JDK7的編譯器無法直接使用該指令,只能通過ASM字節(jié)碼編輯工具調(diào)用。目前主要用于JDK8中java.lang.invoke包和Lambda表達式。
-invokeinteface:調(diào)用接口方法,即由接口類中定義的方法,此時調(diào)用對象聲明的類型是接口類,實際的類型是該接口的某個實現(xiàn)類。假如調(diào)用對象實例是C,查找具體執(zhí)行方法時先在C中查找名稱和描述符都和接口方法一致的方法,如果沒有找到則繼續(xù)在C的父類,父類的父類,不斷往上遞歸查找,直到找到名稱和描述符都和接口方法一致的方法,在編譯正常的情形下通過這兩步查找就可以確認實際被執(zhí)行的方法。
-invokespecial:調(diào)用實例方法,包括父類方法,私有方法和實例初始化方法三種,實際被執(zhí)行的方法的查找邏輯同invokeinteface基本一樣,都是現(xiàn)在當前類查找,再往上遞歸查找當前類的父類,在編譯期即可確認。
-invokestatic:調(diào)用靜態(tài)方法,在方法解析成功后,如果方法所在的類或者接口沒有被初始化則指令執(zhí)行時會觸發(fā)其初始化。因為靜態(tài)方法不能被繼承,因此只需在調(diào)用類中查找是否存在目標方法,也是在編譯期即可確認實際被執(zhí)行的方法。
-invokevirtual:調(diào)用實例方法,依據(jù)調(diào)用對象實例的類型進行分派,如果目標方法是簽名多態(tài)性方法(通常是java.lang.invoke.MethodHanlde的invoke和invokeExact方法),則需要做特殊處理,以保證方法句柄能夠正常調(diào)用。注意invokevirtual并不是其字面描述的一樣的調(diào)用虛方法,調(diào)用某個子類獨有的方法(按C++的虛方法定義這種方法就是非虛方法)也是通過invokevirtual完成,因為JVM無法確認方法調(diào)用實例是該子類的實例,還是該子類的子類實例,后者可能覆寫了該方法。
測試用例如下:
package jni;
interface InterfaceA{
void say();
//接口中的default方法只是接口方法的默認實現(xiàn),接口實現(xiàn)類可以改寫默認實現(xiàn)
default void print(){
System.out.println("InterfaceA default methoed");
}
static void staticDo(){
System.out.println("InterfaceA staticDo");
}
}
interface InterfaceB{
void interfaceDo();
}
class superB implements InterfaceA{
@Override
public void say() {
System.out.println("superB say");
}
@Override
public void print() {
System.out.println("superB print");
}
public void superDo(){
System.out.println("superB superDo ");
}
}
public class InvokeTest extends superB implements InterfaceB{
@Override
public void say() {
super.say();
System.out.println("InvokeTest say");
}
private void privateDo(){
System.out.println("InvokeTest privateDo");
}
public void subDo(){
privateDo();
System.out.println("InvokeTest subDo");
}
@Override
public void interfaceDo() {
System.out.println("InvokeTest interfaceDo");
}
public static void main(String[] args) {
InterfaceA.staticDo();
InvokeTest a=new InvokeTest();
a.say();
a.subDo();
a.print();
a.superDo();
a.interfaceDo();
superB b=a;
b.say();
b.print();
b.superDo();
InterfaceA c=a;
c.say();
c.print();
InterfaceB d=a;
d.interfaceDo();
}
}
編譯過后執(zhí)行javap -v可以查看具體的字節(jié)碼指令,截取部分如下:
public void say();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #2 // Method jni/superB.say:()V 調(diào)用父類方法
4: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #4 // String InvokeTest say
9: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: return
public void subDo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #7 // Method privateDo:()V 調(diào)用私有方法
4: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #8 // String InvokeTest subDo
9: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: return
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
0: invokestatic #10 // InterfaceMethod jni/InterfaceA.staticDo:()V 調(diào)用靜態(tài)方法
3: new #11 // class jni/InvokeTest
6: dup
7: invokespecial #12 // Method "<init>":()V 調(diào)用實例初始化方法
10: astore_1
11: aload_1
12: invokevirtual #13 // Method say:()V 調(diào)用實例的類型都是類,所以這里都是invokevirtual
15: aload_1
16: invokevirtual #14 // Method subDo:()V subDo是子類特有的,為了兼容調(diào)用實例有可能是子類的子類的情形,所以依然使用invokevirtual指令
19: aload_1
20: invokevirtual #15 // Method print:()V
23: aload_1
24: invokevirtual #16 // Method superDo:()V
27: aload_1
28: invokevirtual #17 // Method interfaceDo:()V
31: aload_1
32: astore_2
33: aload_2
34: invokevirtual #2 // Method jni/superB.say:()V
37: aload_2
38: invokevirtual #18 // Method jni/superB.print:()V
41: aload_2
42: invokevirtual #19 // Method jni/superB.superDo:()V
45: aload_1
46: astore_3
47: aload_3
48: invokeinterface #20, 1 // InterfaceMethod jni/InterfaceA.say:()V 調(diào)用實例的類型都是接口類,即使該接口方法已經(jīng)提供了默認實現(xiàn),依然使用invokeinterface
53: aload_3
54: invokeinterface #21, 1 // InterfaceMethod jni/InterfaceA.print:()V
59: aload_1
60: astore 4
62: aload 4
64: invokeinterface #22, 1 // InterfaceMethod jni/InterfaceB.interfaceDo:()V
69: return
2、invokeinteface指令
該指令的實現(xiàn)參考OpenJDK8 hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp 2522行,源代碼說明如下:
CASE(_invokeinterface): {
//pc表示當前字節(jié)碼指令的地址,get_native_u2返回從指定地址開始的2字節(jié)的數(shù)據(jù),將其作為short讀取
//根據(jù)JVM規(guī)范,index表示運行時常量池的索引,指向一個接口方法的符號引用
u2 index = Bytes::get_native_u2(pc+1);
//獲取常量池索引index處的值
ConstantPoolCacheEntry* cache = cp->entry_at(index);
if (!cache->is_resolved((Bytecodes::Code)opcode)) {
//如果接口方法未解析則解析
CALL_VM(InterpreterRuntime::resolve_invoke(THREAD, (Bytecodes::Code)opcode),
handle_exception);
cache = cp->entry_at(index);
}
istate->set_msg(call_method);
//特殊場景下java.lang.Object的虛方法調(diào)用,
if (cache->is_forced_virtual()) {
Method* callee;
//檢查操作數(shù)棧的方法參數(shù)是否為空
CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
//如果是final方法
if (cache->is_vfinal()) {
//獲取解析后的方法
callee = cache->f2_as_vfinal_method();
//final方法調(diào)用
BI_PROFILE_UPDATE_FINALCALL();
} else {
//非final方法
// Get receiver.
int parms = cache->parameter_size();
//獲取執(zhí)行方法調(diào)用的對象實例
oop rcvr = STACK_OBJECT(-parms);
//校驗rcvr是否為空
VERIFY_OOP(rcvr);
//獲取rcvr的實際類型Klass
InstanceKlass* rcvrKlass = (InstanceKlass*)rcvr->klass();
//獲取虛方法表相同索引下的方法
callee = (Method*) rcvrKlass->start_of_vtable()[ cache->f2_as_index()];
//profile統(tǒng)計
BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());
}
//調(diào)用方法
istate->set_callee(callee);
istate->set_callee_entry_point(callee->from_interpreted_entry());
istate->set_bcp_advance(5);
//方法調(diào)用完成返回
UPDATE_PC_AND_RETURN(0); // I'll be back...
}
// this could definitely be cleaned up QQQ
Method* callee;
//獲取接口方法
Method *interface_method = cache->f2_as_interface_method();
//獲取接口類
InstanceKlass* iclass = interface_method->method_holder();
// get receiver
int parms = cache->parameter_size();
//獲取方法調(diào)用對象實例
oop rcvr = STACK_OBJECT(-parms);
CHECK_NULL(rcvr);
//獲取方法調(diào)用對象實例的真實類型
InstanceKlass* int2 = (InstanceKlass*) rcvr->klass();
// Receiver subtype check against resolved interface klass (REFC).
{
//獲取目標接口方法的resolved interface klass
Klass* refc = cache->f1_as_klass();
itableOffsetEntry* scan;
//遍歷int2實現(xiàn)的所有接口類,判斷是否存在目標接口方法對應(yīng)的resolved interface klass
for (scan = (itableOffsetEntry*) int2->start_of_itable();
scan->interface_klass() != NULL;
scan++) {
if (scan->interface_klass() == refc) {
break;
}
}
// int2沒有實現(xiàn)目標resolved interface klass,拋出異常
if (scan->interface_klass() == NULL) {
VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), "", note_no_trap);
}
}
//遍歷int2實現(xiàn)的所有接口類,判斷是否存在目標接口方法對應(yīng)InstanceKlass
itableOffsetEntry* ki = (itableOffsetEntry*) int2->start_of_itable();
int i;
for ( i = 0 ; i < int2->itable_length() ; i++, ki++ ) {
if (ki->interface_klass() == iclass) break;
}
// int2沒有實現(xiàn)目標接口方法的InstanceKlass,拋出異常
if (i == int2->itable_length()) {
VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), "", note_no_trap);
}
//獲取接口方法表的索引
int mindex = interface_method->itable_index();
//獲取rcvr的接口方法表第一個元素
itableMethodEntry* im = ki->first_method_entry(rcvr->klass());
//獲取接口方法表索引mindex處的方法
callee = im[mindex].method();
if (callee == NULL) {
VM_JAVA_ERROR(vmSymbols::java_lang_AbstractMethodError(), "", note_no_trap);
}
//profile統(tǒng)計
BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());
//執(zhí)行方法調(diào)用
istate->set_callee(callee);
istate->set_callee_entry_point(callee->from_interpreted_entry());
//字節(jié)碼指針往后移動5個,_invokeinterface的指令長度是5
istate->set_bcp_advance(5);
UPDATE_PC_AND_RETURN(0); // I'll be back...
}
3、invokevirtual,invokespecial,invokestatic
invokevirtual,invokespecial,invokestatic三個指令的實現(xiàn)都是一樣的,同樣也是參考bytecodeInterpreter.cpp 2634行,源代碼說明如下:
CASE(_invokevirtual):
CASE(_invokespecial):
CASE(_invokestatic): {
//獲取運行時常量池中目標方法的符號引用的索引
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_invoke(THREAD, (Bytecodes::Code)opcode),
handle_exception);
cache = cp->entry_at(index);
}
istate->set_msg(call_method);
{
Method* callee;
//如果是invokevirtual指令
if ((Bytecodes::Code)opcode == Bytecodes::_invokevirtual) {
//判斷方法調(diào)用實例對象是否為空
CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
//如果是final方法
if (cache->is_vfinal()) {
//final方法不需走虛方法表分派,直接使用符號引用解析的結(jié)果
callee = cache->f2_as_vfinal_method();
// Profile final方法調(diào)用統(tǒng)計
BI_PROFILE_UPDATE_FINALCALL();
} else {
// get receiver
int parms = cache->parameter_size();
//獲取方法調(diào)用的實例對象
oop rcvr = STACK_OBJECT(-parms);
VERIFY_OOP(rcvr);
//獲取方法調(diào)用實例對象的實際klass
InstanceKlass* rcvrKlass = (InstanceKlass*)rcvr->klass();
//獲取虛方法表中相同索引處的方法
callee = (Method*) rcvrKlass->start_of_vtable()[ cache->f2_as_index()];
//vitual 調(diào)用 profile統(tǒng)計
BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());
}
} else {
//invokespecial指令
if ((Bytecodes::Code)opcode == Bytecodes::_invokespecial) {
//判斷方法調(diào)用實例對象是否為空
CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
}
//invokespecial指令調(diào)用的方法不需要分派,直接使用符號引用解析的方法
callee = cache->f1_as_method();
// Profile統(tǒng)計
BI_PROFILE_UPDATE_CALL();
}
//執(zhí)行方法調(diào)用
istate->set_callee(callee);
istate->set_callee_entry_point(callee->from_interpreted_entry());
//字節(jié)碼指針往后移動3個,invokevirtual,invokespecial,invokestatic三個指令的指令長度都是3
istate->set_bcp_advance(3);
UPDATE_PC_AND_RETURN(0); // I'll be back...
}
}
從上述源碼分析可知,invokevirtual,invokespecial,invokestatic,invokeinteface四個指令在找到了正確的執(zhí)行方法后,就直接通過JVM解釋器跳轉(zhuǎn)到對應(yīng)方法的執(zhí)行了,跟JNI中方法調(diào)用有很大的不同