第29篇-調用Java主類的main()方法


在第1篇中大概介紹過Java中主類方法main()的調用過程,這一篇介紹的詳細一點,大概的調用過程如下圖所示。

其中淺紅色的函數由主線程執行,而另外的淺綠色部分由另外一個線程執行,這個線程最終也會負責執行Java主類中的main()方法。在JavaMain()函數中調用LoadMainClass()函數加載Java主類。接着在JavaMain()函數中有如下調用:

源代碼位置:openjdk/jdk/src/share/bin/java.c

mainID = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V");

env為JNIEnv*類型。調用JNIEnv類型中定義的GetStaticMethodID()函數獲取Java主類中main()方法的方法唯一ID,調用GetStaticMethodID()函數就是調用到jni_GetStaticMethodID()函數,實現如下:

源代碼位置:openjdk/hotspot/src/share/vm/prims/jni.cpp

JNI_ENTRY(jmethodID, jni_GetStaticMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig))
  jmethodID ret = get_method_id(env, clazz, name, sig, true, thread);
  return ret;
JNI_END


static jmethodID get_method_id(
   JNIEnv     *env,
   jclass     clazz,
   const char *name_str,
   const char *sig,
   bool       is_static,
   TRAPS
){
  const char *name_to_probe = (name_str == NULL)
                        ? vmSymbols::object_initializer_name()->as_C_string()
                        : name_str;
  TempNewSymbol name = SymbolTable::probe(name_to_probe, (int)strlen(name_to_probe));
  TempNewSymbol signature = SymbolTable::probe(sig, (int)strlen(sig));

  KlassHandle klass(THREAD,java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)));

  // 保證java.lang.Class類已經初始化完成
  klass()->initialize(CHECK_NULL);

  Method* m;
  if ( name == vmSymbols::object_initializer_name() || 查找的是<init>方法
       name == vmSymbols::class_initializer_name() ) { 查找的是<clinit>方法
    // 因為要查找的是構造函數,構造函數沒有繼承特性,所以當前類找不到時不向父類中繼續查找
    if (klass->oop_is_instance()) {
       // find_method()函數不會向上查找
       m = InstanceKlass::cast(klass())->find_method(name, signature); 
    } else {
       m = NULL;
    }
  } else {
    // lookup_method()函數會向上查找
    m = klass->lookup_method(name, signature); 
    if (m == NULL &&  klass->oop_is_instance()) {
       m = InstanceKlass::cast(klass())->lookup_method_in_ordered_interfaces(name, signature);
    }
  }
  return m->jmethod_id();
}  

獲取Java類中main()方法的jmethod_id。

源代碼位置:method.hpp
// Get this method's jmethodID -- allocate if it doesn't exist
jmethodID jmethod_id()  {
	  methodHandle this_h(this);
      return InstanceKlass::get_jmethod_id(method_holder(), this_h);
}

調用的InstanceKlass::get_jmethod_id()函數獲取唯一ID,關於如何獲取或生成ID的過程這里不再詳細介紹,有興趣的自行研究。

在JavaMain()函數中有如下調用:

mainArgs = CreateApplicationArgs(env, argv, argc);

(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);

通過調用CallStaticVoidMethod()函數來調用Java主類中的main()方法。控制權轉移到JavaMainClass.main()方法之中,等JavaMainClass.main()方法返回之后當前線程才接手過來清理和關閉HotSpot VM。

源代碼位置:openjdk/hotspot/src/share/vm/prims/jni.cpp 

JNI_ENTRY(void, jni_CallStaticVoidMethod(JNIEnv *env, jclass cls, jmethodID methodID, ...))
  va_list args;
  va_start(args, methodID);
  JavaValue jvalue(T_VOID);

  JNI_ArgumentPusherVaArg  ap(methodID, args);
  jni_invoke_static(env, &jvalue, NULL, JNI_STATIC, methodID, &ap, CHECK);
  va_end(args);
JNI_END
  

va_list與va_start是變長參數解析時需要用到的宏。將傳給Java方法的參數以C的可變長度參數傳入后,使用JNI_ArgumentPusherVaArg實例ap是將其封裝起來。JNI_ArgumentPusherVaArg類的繼承體系如下:

JNI_ArgumentPusherVaArg->JNI_ArgumentPusher->SignatureIterator

調用的jni_invoke_static()函數的實現如下:

// 通過jni的方式調用Java靜態方法
static void jni_invoke_static(
 JNIEnv              *env,
 JavaValue*          result,
 jobject             receiver,
 JNICallType         call_type,
 jmethodID           method_id,
 JNI_ArgumentPusher  *args,
 TRAPS
){
  Method* m = Method::resolve_jmethod_id(method_id);
  methodHandle method(THREAD, m);

  ResourceMark rm(THREAD);
  int number_of_parameters = method->size_of_parameters();
  // 這里進一步將要傳給Java的參數轉換為JavaCallArguments對象傳下去
  JavaCallArguments  java_args(number_of_parameters);
  args->set_java_argument_object(&java_args);

  // Fill out(填,填寫) JavaCallArguments object
  Fingerprinter fp =  Fingerprinter(method);
  uint64_t x =  fp.fingerprint();
  args->iterate(x);
  // Initialize result type
  BasicType bt = args->get_ret_type();
  result->set_type(bt);

  // Invoke the method. Result is returned as oop.
  JavaCalls::call(result, method, &java_args, CHECK);

  // Convert result
  if (
    result->get_type() == T_OBJECT || 
    result->get_type() == T_ARRAY
  ) {
     oop tmp =  (oop) result->get_jobject();
     jobject jobj = JNIHandles::make_local(env,tmp);
     result->set_jobject(jobj);
  }
}

通過JavaCalls::call()函數來調用Java主類的main()方法。關於JavaCalls::call()函數大家應該不會陌生,這個函數是怎么建立Java棧幀以及找到Java方法入口在之前詳細介紹過。 

推薦閱讀:

第1篇-關於JVM運行時,開篇說的簡單些

第2篇-JVM虛擬機這樣來調用Java主類的main()方法

第3篇-CallStub新棧幀的創建

第4篇-JVM終於開始調用Java主類的main()方法啦

第5篇-調用Java方法后彈出棧幀及處理返回結果

第6篇-Java方法新棧幀的創建

第7篇-為Java方法創建棧幀

第8篇-dispatch_next()函數分派字節碼

第9篇-字節碼指令的定義

第10篇-初始化模板表

第11篇-認識Stub與StubQueue

第12篇-認識CodeletMark

第13篇-通過InterpreterCodelet存儲機器指令片段

第14篇-生成重要的例程

第15章-解釋器及解釋器生成器

第16章-虛擬機中的匯編器

第17章-x86-64寄存器

第18章-x86指令集之常用指令

第19篇-加載與存儲指令(1)

第20篇-加載與存儲指令之ldc與_fast_aldc指令(2)

第21篇-加載與存儲指令之iload、_fast_iload等(3)

第22篇-虛擬機字節碼之運算指令

第23篇-虛擬機字節碼指令之類型轉換

第24篇-虛擬機對象操作指令之getstatic

第25篇-虛擬機對象操作指令之getfield

第26篇-虛擬機對象操作指令之putstatic

第27篇-虛擬機字節碼指令之操作數棧管理指令

第28篇-虛擬機字節碼指令之控制轉移指令

 

 

  

  

 

 

 

 

 

 

 

 

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



猜您在找 第4篇-JVM終於開始調用Java主類的main()方法啦 java 主類的main方法調用其他方法 Java中是否可以調用一個類中的main方法? java main方法也可以被其他方法調用 4.編寫Java應用程序。首先,定義一個時鍾類——Clock,它包括三個int型 成員變量分別表示時、分、秒,一個構造方法用於對三個成員變量(時、分、秒) 進行初始化,還有一個成員方法show()用於顯示時鍾對象的時間。其次,再定義 一個主類——TestClass,在主類的main方法中創建多個時鍾類的對象,使用這 些對象調用方法show()來顯示時鍾的時間。 編寫一個類A,該類創建的對象可以調用方法f輸出小寫的英文字母表。然 后再編寫一個A類的子類B,要求子類B必須繼承類A的方法f(不允許重寫), 子類B創建的對象不僅可以調用方法f輸出小寫的英文字母表,而且可以調用子 類新增的方法g輸出大寫的英文字母表。最后編寫主類C,在主類的main方法 中測試類A與類B。 【Java入門提高篇】Day29 Java容器類詳解(十一)LinkedHashSet詳解 29個android開發常用的類、方法及接口 編寫Java應用程序。首先,定義描述學生的類——Student,包括學號(int)、 姓名(String)、年齡(int)等屬性;二個方法:Student(int stuNo,String name,int age) 用於對對象的初始化,outPut()用於輸出學生信息。其次,再定義一個主類—— TestClass,在主類的main方法中創建多個Student類的對象,使用這些對象來測 試Stud JAVA 主函數(主方法)
 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM