ART深入浅出1-仮想マシンの起動と初期化


本文はAndroid 7.1に基づいているが、BSPから入手したバージョンは少し異なるため、本稿で述べたソースコードは読者が見つけたソースコードと完全に一致するとは限らない.この文書では、ソースコードの断片を提供する際に、行番号が一致しない場合は、クラス名と関数名を参照して対応するソースコードを見つけます.
仮想マシンの起動
Androidのアプリケーションとサービスはzygoteプロセスによって生成されることを知っています.zygoteプロセスはjava仮想マシン環境を作成し、zygoteのjavaエントリを呼び出します.
zygoteプログラムのメイン関数、ファイルframeworks/base/cmds/app_に置くprocess/app_main.cpp中.main関数を簡単に読むと,すべての作業がAppRuntimeに引き継がれていることが分かった.AppRuntimeクラスのコア関数startは、仮想マシンを起動し、指定したmain関数を実行します.
frameworks/base/cmds/app_process/app_main.cpp:306 main
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {

runtimeはAppRuntimeです.AppRuntimeはAndroid Runtimeから継承され、start関数もAndroid Runtimeで実現されます.
Android Runtimeクラスframeworks/base/include/android_runtime/AndroidRuntime.hにおいて、frameworks/base/core/jni/Android Runtime.cppで実現します.
start関数の実装に重点を置いた.AndroidRuntime::startはAndroidRuntime::startVm関数を呼び出し、仮想マシンを起動します.startVm関数は2つの部分に分かれている:1.パラメータの準備;2. CreateJavaVM.
 frameworks/base/core/jni/AndroidRuntime.cpp:948 AndroidRuntime::startVm
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed
"); return -1; }

JNI_CreateJavaVM関数のプロトタイプは
libnativehelper/include/nativehelper/jni.h:1103
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);

パラメータinitArgsのタイプはJavaVMInitArgsで、以下のように定義されています.
libnativehelper/include/nativehelper/jni.h:1081
typedef struct JavaVMOption {
    const char* optionString;
    void*       extraInfo;
} JavaVMOption;

typedef struct JavaVMInitArgs {
    jint        version;    /* use JNI_VERSION_1_2 or later */

    jint        nOptions;
    JavaVMOption* options;
    jboolean    ignoreUnrecognized;
} JavaVMInitArgs;

libnativehelperこれはnativeライブラリ支援ロードクラスです.Androidはこのライブラリでdalvikとartを隔離した.もちろん7.1ではartしかありません.このライブラリは単純なパッケージにすぎないと思います.
ART内の入口
今、私たちはやっとARTの内部に入った.ART内のJNI_CreateJavaVM関数は、実装を2つの部分に分けることができます:1.RuntimeOptionsオブジェクトを初期化し、パラメータを保存します.2.Runtimeを起動します.
RuntimeはARTでjavaランタイム環境を表します.1つのプロセスにはART仮想マシンが1つしか作成されず、1つのART仮想マシンにはRuntimeが1つしかありません.JNIを見てみましょうCreateJavaVM関数の断片化
art/runtime/java_vm_ext.cc:939 JNI_CreateJavaVM
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  ScopedTrace trace(__FUNCTION__);
  const JavaVMInitArgs* args = static_cast(vm_args);
  if (IsBadJniVersion(args->version)) {
    LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
    return JNI_EVERSION;
  }
  RuntimeOptions options;
  for (int i = 0; i < args->nOptions; ++i) {
    JavaVMOption* option = &args->options[i];
    options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
  }
  bool ignore_unrecognized = args->ignoreUnrecognized;
  if (!Runtime::Create(options, ignore_unrecognized)) {
    return JNI_ERR;
  }

 ...//960
  Runtime* runtime = Runtime::Current();
  bool started = runtime->Start();
 ...
}

ここで注目すべきはRuntime::Create関数とRuntime::Start関数です.
Runtimeの作成と起動
Runtime::Create関数(art/runtime/runtime.cc:486)はRuntimeインスタンスを作成し、そのInitメソッドを呼び出します.
Runtime::Init
Initメソッドはいろいろなことをしましたが、まとめてみると、次のようなものがあります.
  • サブモジュールの初期化、例えばMemMap::Init、これは初期化メモリマッピングモジュールである.QuasiAtomic::Startupなど.この部分にはあまり注目すべきものはありません.
  • キー変数を設定します.
  • boot_class_path_string_
  • class_path_string_
  • compiler_callbacks_ : コンパイル用バックエンドオブジェクト
  • compiler_executable_ :dex 2 oatの経路
  • image_location_ : boot.art等imageの経路
  • monitor_list_/monitor_pool_: synchronizedキーワードを実装するオブジェクト
  • です.
  • thread_list_: JAvaのthread管理クラス
  • intern_table_:文字列テーブル
  • heapオブジェクトを作成します.heap_=new gc::Heap(...).heapの作成中にimageがロードされます.
  • 独自の信号処理関数、InitPlatformSignalHandlers()を作成します.いくつかの運転時異常、例えばNull PointerException、StackOverflowExeptionなど、ARTはlinux信号メカニズムによって実現される額である.たとえば、ARTはSIGSEGV信号を切り取り、空のポインタの問題であり、空のポインタが発生したコードがjavaコードにあることを発見すると、Null PointerExceptionが放出され、プログラムを実行し続けます.
  • ClassLinkerオブジェクトを作成します.これは非常に重要なオブジェクトであり、クラスのロード、リンク、初期化はこのクラスで完了します.

  • Runtime::Initは実はたくさんのことをしました.本小結内では一つ一つ説明するのは難しい.各サブユニットについては後述するが,それらの初期化過程について詳細に説明する.
    Runtime::Start
    Runtime::Initが仮想マシンを初期化した場合、Runtime::Startはjavaコードを実行するために準備されています.
    この関数は大きくありませんが、非常に多くのことをしました.以下、キーコードもリストします.
    art/runtime/runtime.cc:566
    bool Runtime::Start() {
      ....//585
    
      // Create the JIT either if we have to use JIT compilation or save profiling info.
      // TODO(calin): We use the JIT class as a proxy for JIT compilation and for
      // recoding profiles. Maybe we should consider changing the name to be more clear it's
      // not only about compiling. b/28295073.
      if (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) {
        std::string error_msg;
        if (!IsZygote()) {
        // If we are the zygote then we need to wait until after forking to create the code cache
        // due to SELinux restrictions on r/w/x memory regions.
          CreateJit();
        } else if (jit_options_->UseJitCompilation()) {
          if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
            // Try to load compiler pre zygote to reduce PSS. b/27744947
            LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
          }
        }
      }
    
      if (!IsImageDex2OatEnabled() || !GetHeap()->HasBootImageSpace()) {
        ScopedObjectAccess soa(self);
        StackHandleScope<2> hs(soa.Self());
    
        auto class_class(hs.NewHandle<:class>(mirror::Class::GetJavaLangClass()));
        auto field_class(hs.NewHandle<:class>(mirror::Field::StaticClass()));
    
        class_linker_->EnsureInitialized(soa.Self(), class_class, true, true);
        // Field class is needed for register_java_net_InetAddress in libcore, b/28153851.
        class_linker_->EnsureInitialized(soa.Self(), field_class, true, true);
      }
    
      // InitNativeMethods needs to be after started_ so that the classes
      // it touches will have methods linked to the oat file if necessary.
      {
        ScopedTrace trace2("InitNativeMethods");
        InitNativeMethods();
      }
    
      // Initialize well known thread group values that may be accessed threads while attaching.
      InitThreadGroups(self);
    
      Thread::FinishStartup();
    
      system_class_loader_ = CreateSystemClassLoader(this);
    
      if (is_zygote_) {
        if (!InitZygote()) {
          return false;
        }
      } else {
     ....//644
      }
    
      StartDaemonThreads();
    
     ....
    }

    重要な初期化は次のとおりです.
  • CreateJit:Android 6.0からJIT(Just In Time)を導入し、7.0で大規模な使用を開始する.
  • 基本クラスclassを初期化class、すなわちjava.lang.Classクラス、これは最も基礎的なクラスです.
  • InitNativeMethods():java.langの下の最も基礎的なクラスのJNIインタフェースは初期化します;
  • InitZygote:zygoteプログラムが呼び出されるときに必要なことです.app_main.cppファイルはzygoteのメインプログラムだけでなく、アンドロイドのamコマンドラインのメインプログラムでもあるので、ここで区別しました.
  • StartDemonThreads、javaを呼び出します.lang.Daemons.startは、GCの実行、finalizeメソッドの呼び出し、ReferenceQueueの処理などの一連の作業を行うスレッドのセットを起動します.

  • ここで注意しなければならないのは、zygoteとamプログラムだけがJNIを呼び出すことです.CreateJavaVM関数は、zygoteによって孵化された他のプロセスがこのエントリ関数を実行するのではなく、Runtime::PreZygoteForkとRuntime::InitNonZygoteOrPostForkを実行します.この点は後述するが,ARTがzygote孵化子プロセスをどのようにサポートしているかを専門に紹介する.