art虚拟机启动过程分析

标签(空格分隔): art android5.1 启动流程 jvm


我们都已经知道,Android系统是基于Linux内核,而应用程序大都由Java语言实现的一个操作系统,包含一套C/C++ Framework和Java Framework以及其他的库支持文件和Hal层的一些东西。Android的启动过程也是从/system/core/init/init.c的main函数开始(这里忽略了bootloader的引导以及Linux Kernel的启动)。init.c这个文件做的事情比较多,这里我们只关注和Java层有关的东西。init.c中解析init.rc文件,在init.rc中和Java世界相关的是zygote,它在一个脚本文件中被系统创建出来,如下

    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

在Linux系统下,app_process是一个bin类型的可执行文件,它的源码在/frameworks/base/cmds/app_process中,其程序入口为main函数。在分析之前,我们先将这个过程的时序图和涉及到的一些类信息简单看看,有个宏观的感知。

时序图如下:
<img src="http://o767chiwj.bkt.clouddn.com/jvm.png"/>

相关类图:
<img src="http://o767chiwj.bkt.clouddn.com/javavm.png"/>
JavaVM:进程相关,每个虚拟机进程有持有一个JavaVM对象
JNIEnv:线程相关,每个虚拟机线程有持有一个JNIEnv对象
Runtime:Java程序运行时环境
Thread:Java Thread对象

Step1: app_process.main (/frameworks/base/cmds/app_process)###

由命令行./system/bin/app_process -Xzygote /system/bin --zygote --start-system-server调用。

int main(int argc, char* const argv[])
{
    ....
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    argc--;
    argv++;
    int i;
    for (i = 0; i < argc; i++) {
        if (argv[i][0] != '-') {
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }
        runtime.addOption(strdup(argv[i]));
    }
    ....
    // Parse runtime arguments.  Stop at first unrecognized option.
    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }
    ....
    if (!className.isEmpty()) {
        ....
    } else {
        // We're in zygote mode.
        maybeCreateDalvikCache();
        ...
    }
    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string());
        set_process_name(niceName.string());
    }
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
    }
}

代码先是创建了一个AppRuntime对象,它继承自AndroidRuntime,构造函数参数分别是命令行字符串的首地址和字符串的总长度,然后将这两个值存放到其父类的成员变量mArgBlockStart和mArgBlockLength中,这里分析下AndroidRuntime成员变量mOptions,相关定义为

    Vector<JavaVMOption> mOptions;
    typedef struct JavaVMOption {
        const char* optionString;
        void*       extraInfo;
    } JavaVMOption;

可知mOptions就是用来保存这些JavaVMOption参数的,其成员函数addOption负责往mOptions中添加元素,main函数中首先将命令行中第一个以"-"开头的字符串加入到mOptions中,这里即是"-Xzygote",继续解析命令行参数,while循环完成后,此时zygote和startSystemServer为true niceName为“zygote”,表示当前是在zygote模式下,接下来调用maybeCreateDalvikCache方法,创建这个/data/dalvik-cache/x86文件(我这里是x86的架构,还有arm等),存放字节码的缓存文件。接下来设置abi的相关信息,args最终存放的就是["start-system-server", "--abi-list=x86"]。此时niceName非空,接下来调用runtime.setArgv0,同时设置当前进程的name为niceName。

    void AndroidRuntime::setArgv0(const char* argv0) {
        memset(mArgBlockStart, 0, mArgBlockLength);
        strlcpy(mArgBlockStart, argv0, mArgBlockLength);
    }

这个函数调用memeset清除这块内存区,同时将niceName copy放置到mArgBlockStart的地址空间处。最后由于zygote为true,会进入runtime.start函数,此时args参数为"start-system-server", "--abi-list=x86"。

Step2: AppRuntime::start (/frameworks/base/core/jni/AndroidRuntime.cpp)###

AppRuntime继承至AndroidRuntime,其start方法实现如下:

void AndroidRuntime::start(const char* className, const Vector<String8>& options)
{
    ....
    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    free(slashClassName);

    ALOGD("Shutting down VM\n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)
        ALOGW("Warning: VM did not shut down cleanly\n");
}

这个方法控制了整个启动的流程,主要由以下几步:
->创建JniInvocation,加载art虚拟机so文件(libart.so)。
->调用函数startVM,创建JavaVM和JNIEnv
->注册Android jni方法
->启动到Java世界
下面分段阅读这几个过程。

Step3: JniInvocation::Init (/libnativehelper/JniInvocation.cpp)###

该函数中首先获取要加载的library名字,默认是libart.so库文件,并打开这个动态库并解析出所有未定义符号,同时返回动态链接库的句柄给handle_指针变量。接下来继续解析,将JNI_GetDefaultJavaVMInitArgs_、JNI_CreateJavaVM_、JNI_GetCreatedJavaVMs_三个函数指针进行初始化。生成libart.so的源代码位于/art/runtime,其中在jni_internal.cc中定义有这三个函数。

bool JniInvocation::Init(const char* library) {
  ....
  library = GetLibrary(library, buffer);
  handle_ = dlopen(library, RTLD_NOW);
  ....
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
                  "JNI_GetDefaultJavaVMInitArgs")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
                  "JNI_CreateJavaVM")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
                  "JNI_GetCreatedJavaVMs")) {
    return false;
  }
  return true;
}

函数执行完毕之后,返回至AndroidRuntime的start,将控制权归还,接着执行start中的startVM函数。

Step4: AndroidRuntime::startVm (/frameworks/base/core/jni/AndroidRuntime.cpp)###

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
    int result = -1;
    JavaVMInitArgs initArgs;
    ....
    initArgs.version = JNI_VERSION_1_4;
    initArgs.options = mOptions.editArray();
    initArgs.nOptions = mOptions.size();
    initArgs.ignoreUnrecognized = JNI_FALSE;

    /*
     * Initialize the VM.
     *
     * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
     * If this call succeeds, the VM is ready, and we can start issuing
     * JNI calls.
     */
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        goto bail;
    }
    result = 0;
bail:
    return result;
}

这个函数中,通过property_get获取系统属性文件,设置大量关于虚拟机的一些参数信息,addOption、parseExtraOpts、parseRuntimeOption、parseCompilerRuntimeOption这四个函数均是往mOptions这个容器中添加元素,经过一些列的参数设置,在6-9行将mOptions信息保存至结构体initArgs中,initArgs是一个JavaVMInitArgs结构体对象

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

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

继续往下,接着调用JNI_CreateJavaVM去进一步创建JavaVM对象。

Step5: JniInvocation.JNI_CreateJavaVM (/libnativehelper/JniInvocation.cpp)###

extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
  return JniInvocation::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count);
}

jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  return JNI_CreateJavaVM_(p_vm, p_env, vm_args);
}

JNI_GetCreatedJavaVMs是类JniInvocation的一个友元函数,在它里面继续调用JniInvocation类的JNI_CreateJavaVM方法,在这个方法中,通过函数指针JNI_CreateJavaVM_去调用libart.so动态库中的JNI_CreateJavaVM函数。

Step6: jni_internal.JNI_CreateJavaVM (/art/runtime/jni_internal.cc)###

extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(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;
  }
  Runtime* runtime = Runtime::Current();
  bool started = runtime->Start();
  if (!started) {
    delete Thread::Current()->GetJniEnv();
    delete runtime->GetJavaVM();
    LOG(WARNING) << "CreateJavaVM failed";
    return JNI_ERR;
  }
  *p_env = Thread::Current()->GetJniEnv();
  *p_vm = runtime->GetJavaVM();
  return JNI_OK;
}

这个函数中,先接收传过来的JavaVMInitArgs类型的参数,然后检查jni版本是否合法。接着创建RuntimeOptions的options对象,去保存前面存储在JavaVMInitArgs options的参数,options中存放的是前面类AndroidRuntime成员变量Vector<JavaVMOption> mOptions这个数组的首地址。因此代码中for循环一次去取这些参数信息,并push到当前RuntimeOptions的options对象中,RuntimeOptions存放的元素pair实质上是一个结构体,其主要的两个成员变量是first和second就是一个键值对的存放形式,主要的作用是将两个数据组合成一个数据。其定义为

typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions;

参数全部存放至options后,接着开始着手创建Runtime对象,并进行一些初始工作,它描述了JavaVM运行时的所有的信息,也即是我们本文的另一个主角。函数会去调用Runtime::Create和Runtime::Start()去完成这些工作。

Step7: Runtime::Create (/art/runtime/runtime.cc)###

函数先是初始化log工具,而后创建一个Runtime实例新建Runtime实例是其构造函数很简单,只是初始化了一些成员变量信息,接着通过前面设置的RuntimeOptions参数,调用实例的Init函数去检查ignore_unrecognized是否通过,并做一些其他初始化工作。

Step8: Runtime::Init (/art/runtime/runtime.cc)###

bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) {
    MemMap::Init();
    ....
    heap_ = new gc::Heap(options->heap_initial_size_,
                       options->heap_growth_limit_,
                       options->heap_min_free_,
                       options->heap_max_free_,
                       ....);
    ...
    BlockSignals();
    InitPlatformSignalHandlers();
    ...
    InitializeSignalChain();
    ...
    java_vm_ = new JavaVMExt(this, options.get());
    Thread::Startup();
    Thread* self = Thread::Attach("main", false, nullptr, false);
      // Set us to runnable so tools using a runtime can allocate and GC by default
    self->TransitionFromSuspendedToRunnable();

    // Now we're attached, we can take the heap locks and validate the heap.
    GetHeap()->EnableObjectValidation();
    class_linker_ = new ClassLinker(intern_table_);
    if (GetHeap()->HasImageSpace()) {
        class_linker_->InitFromImage();
        if (kIsDebugBuild) {
            GetHeap()->GetImageSpace()->VerifyImageAllocations();
        }
    } 
    ....
    CHECK(class_linker_ != nullptr);
    verifier::MethodVerifier::Init();
    ....
    if (options->method_trace_) {
        ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart);
        Trace::Start(options->method_trace_file_.c_str(), -1, options->method_trace_file_size_, 0,false, false, 0);
    }
    ....
    is_native_bridge_loaded_ = LoadNativeBridge(options->native_bridge_library_filename_);
    VLOG(startup) << "Runtime::Init exiting";
    return true;
}

这个函数先是调用了MemMap的Init函数,在Init函数中新建了一个Maps对象,分析下它的数据结构,

void MemMap::Init() {
  MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
  if (maps_ == nullptr) {
    // dex2oat calls MemMap::Init twice since its needed before the runtime is created.
    maps_ = new Maps;
  }
}

typedef AllocationTrackingMultiMap<void*, MemMap*, kAllocatorTagMaps> Maps;

template<class Key, class T, AllocatorTag kTag, class Compare = std::less<Key>>
class AllocationTrackingMultiMap : public std::multimap<
    Key, T, Compare, TrackingAllocator<std::pair<Key, T>, kTag>> {
};

分析Maps这个对象之前,我们先来熟悉下几个std的数据结构
下面先介绍几个std类型的结构

template < class Key,
    class T,
    class Compare = less<Key>,
    class Alloc = allocator<pair<const Key,T> >
> class multimap;

template <class T> struct less;
template <class T1, class T2> struct pair;
template <class T> class allocator;

std::multimap
多键映射表容器是一个按特定顺序存储以键值对组合而成的元素的关联容器,容器中元素的主键可以相等,
Key : 主键的类型
T : 被映射的值的类型
Compare :一个二元谓词,以两个元素的主键为参数返回一个 bool 值。可以是函数指针类型或函数对象类型
Alloc : 容器内部用来管理内存分配及释放的内存分配器的类型。这个参数是可选的,它的默认值是 std::allocator<T>
std::less : 用做比较,等价于 x < y
std::pair : 实现了二元组
std::allocator : 默认的内存分配器

将类型替换,得到Maps实际上为如下类型

class AllocationTrackingMultiMap : public std::multimap<
    void*, MemMap*, std::less<void*>, TrackingAllocator<std::pair<void*, MemMap*>, kAllocatorTagMaps>> {
};

class TrackingAllocator : public TrackingAllocatorImpl<std::pair<void*, MemMap*>, kAllocatorTagMaps>{
};

class TrackingAllocatorImpl : public std::allocator<std::pair<void*, MemMap*>> {
};

这里用了大量的c++模板,其实就是代码的一种简写,生成实际的类型,这和Java的模板有些区别,从这里可以知道Maps的类型为一个多键映射表容器,其中主键Key类型为(void ),被映射的值的类型T为(MemMap ),二元谓词Compare为less<void>,内存分配器类型为allocator<pair<void, MemMap*>>,暂且先明白Maps就是这么一个多键映射表容器。
接下来用ParsedOptions开始解析前文的一些虚拟机参数,进行一些设置,先帖上一段我机器的开机log信息,关于vm这些options默认的设置,不同机器可能不一样。

I/art     (  122): option[0]=-Xzygote
I/art     (  122): option[1]=-Xstacktracefile:/data/anr/traces.txt
I/art     (  122): option[2]=exit
I/art     (  122): option[3]=vfprintf
I/art     (  122): option[4]=sensitiveThread
I/art     (  122): option[5]=-verbose:gc
I/art     (  122): option[6]=-Xms8m
I/art     (  122): option[7]=-Xmx256m
I/art     (  122): option[8]=-XX:mainThreadStackSize=24K
I/art     (  122): option[9]=-XX:HeapGrowthLimit=96m
I/art     (  122): option[10]=-XX:HeapMinFree=512k
I/art     (  122): option[11]=-XX:HeapMaxFree=8m
I/art     (  122): option[12]=-XX:HeapTargetUtilization=0.75
I/art     (  122): option[13]=-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y
I/art     (  122): option[14]=-Xlockprofthreshold:500
I/art     (  122): option[15]=-Ximage-compiler-option
I/art     (  122): option[16]=--runtime-arg
I/art     (  122): option[17]=-Ximage-compiler-option
I/art     (  122): option[18]=-Xms64m
I/art     (  122): option[19]=-Ximage-compiler-option
I/art     (  122): option[20]=--runtime-arg
I/art     (  122): option[21]=-Ximage-compiler-option
I/art     (  122): option[22]=-Xmx64m
I/art     (  122): option[23]=-Ximage-compiler-option
I/art     (  122): option[24]=--image-classes-zip=/system/framework/framework.jar
I/art     (  122): option[25]=-Ximage-compiler-option
I/art     (  122): option[26]=--image-classes=preloaded-classes
I/art     (  122): option[27]=-Xcompiler-option
I/art     (  122): option[28]=--runtime-arg
I/art     (  122): option[29]=-Xcompiler-option
I/art     (  122): option[30]=-Xms64m
I/art     (  122): option[31]=-Xcompiler-option
I/art     (  122): option[32]=--runtime-arg
I/art     (  122): option[33]=-Xcompiler-option
I/art     (  122): option[34]=-Xmx512m
I/art     (  122): option[35]=-Duser.language=en
I/art     (  122): option[36]=-Duser.region=US

image_file_name : 为/system/framework/boot.art,boot.art是一个img文件,直接被映射到ART虚拟机的堆空间中,包含了boot.oat中的某些对象实例以及函数地址,由dex2oat命令将Android系统必须的的jar包编译生成的,同时生成的boot.oat文件可以将其理解为ART虚拟机的启动类。
image_instruction_set : 是要运行的应用程序所支持的指令集,而kRuntimeISA是指当前Android系统所运行平台的指令集,它是在编译时就指定好了的,主要由kArm、kArm64、kMips、kX86、kX86_64,接下来根据相关参数信息创建一个Heap对象。它是管理JVM堆内存和gc的重要对象。

Step9: Heap::Heap (/art/runtime/gc/heap.cc)###

Heap::Heap(......,
           size_t capacity, size_t non_moving_space_capacity, const std::string& image_file_name,
           const InstructionSet image_instruction_set, CollectorType foreground_collector_type,
           CollectorType background_collector_type, ......) : ..... {
  ....
  ChangeCollector(desired_collector_type_);
  live_bitmap_.reset(new accounting::HeapBitmap(this));
  mark_bitmap_.reset(new accounting::HeapBitmap(this));
  // Requested begin for the alloc space, to follow the mapped image and oat files
  byte* requested_alloc_space_begin = nullptr;
  if (!image_file_name.empty()) {
    std::string error_msg;
    space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(),
                                                               image_instruction_set,
                                                               &error_msg);
    if (image_space != nullptr) {
      AddSpace(image_space);
      // Oat files referenced by image files immediately follow them in memory, ensure alloc space
      // isn't going to get in the middle
      byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd();
      CHECK_GT(oat_file_end_addr, image_space->End());
      requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
    } else {
      LOG(WARNING) << "Could not create image space with image file '" << image_file_name << "'. "
                   << "Attempting to fall back to imageless running. Error was: " << error_msg;
    }
  }
 .....
  bool separate_non_moving_space = is_zygote ||
      support_homogeneous_space_compaction || IsMovingGc(foreground_collector_type_) ||
      IsMovingGc(background_collector_type_);
  .....
  if (separate_non_moving_space) {
    // If we are the zygote, the non moving space becomes the zygote space when we run
    // PreZygoteFork the first time. In this case, call the map "zygote space" since we can't
    // rename the mem map later.
    const char* space_name = is_zygote ? kZygoteSpaceName: kNonMovingSpaceName;
    // Reserve the non moving mem map before the other two since it needs to be at a specific
    // address.
    non_moving_space_mem_map.reset(
        MemMap::MapAnonymous(space_name, requested_alloc_space_begin,
                             non_moving_space_capacity, PROT_READ | PROT_WRITE, true, &error_str));
    CHECK(non_moving_space_mem_map != nullptr) << error_str;
    // Try to reserve virtual memory at a lower address if we have a separate non moving space.
    request_begin = reinterpret_cast<byte*>(300 * MB);
  }
  // Attempt to create 2 mem maps at or after the requested begin.
  main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, capacity_,
                                                    PROT_READ | PROT_WRITE, &error_str));
  CHECK(main_mem_map_1.get() != nullptr) << error_str;
  if (support_homogeneous_space_compaction ||
      background_collector_type_ == kCollectorTypeSS ||
      foreground_collector_type_ == kCollectorTypeSS) {
    main_mem_map_2.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[1], main_mem_map_1->End(),
                                                      capacity_, PROT_READ | PROT_WRITE,
                                                      &error_str));
    CHECK(main_mem_map_2.get() != nullptr) << error_str;
  }
  // Create the non moving space first so that bitmaps don't take up the address range.
  if (separate_non_moving_space) {
    // Non moving space is always dlmalloc since we currently don't have support for multiple
    // active rosalloc spaces.
    const size_t size = non_moving_space_mem_map->Size();
    non_moving_space_ = space::DlMallocSpace::CreateFromMemMap(
        non_moving_space_mem_map.release(), "zygote / non moving space", kDefaultStartingSize,
        initial_size, size, size, false);
    non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
    CHECK(non_moving_space_ != nullptr) << "Failed creating non moving space "
        << requested_alloc_space_begin;
    AddSpace(non_moving_space_);
  }
  // Create other spaces based on whether or not we have a moving GC.
  if (IsMovingGc(foreground_collector_type_) && foreground_collector_type_ != kCollectorTypeGSS) {
    // Create bump pointer spaces.
    // We only to create the bump pointer if the foreground collector is a compacting GC.
    // TODO: Place bump-pointer spaces somewhere to minimize size of card table.
    bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 1",
                                                                    main_mem_map_1.release());
    CHECK(bump_pointer_space_ != nullptr) << "Failed to create bump pointer space";
    AddSpace(bump_pointer_space_);
    temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
                                                            main_mem_map_2.release());
    CHECK(temp_space_ != nullptr) << "Failed to create bump pointer space";
    AddSpace(temp_space_);
    CHECK(separate_non_moving_space);
  }
  ......
  // Allocate the large object space.
  if (kUseFreeListSpaceForLOS) {
    large_object_space_ = space::FreeListSpace::Create("large object space", nullptr, capacity_);
  } else {
    large_object_space_ = space::LargeObjectMapSpace::Create("large object space");
  }
  CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
  AddSpace(large_object_space_);
  ......
  // Allocate the card table.
  card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity));
  CHECK(card_table_.get() != NULL) << "Failed to create card table";
  // Card cache for now since it makes it easier for us to update the references to the copying
  // spaces.
  accounting::ModUnionTable* mod_union_table =
      new accounting::ModUnionTableToZygoteAllocspace("Image mod-union table", this,
                                                      GetImageSpace());
  CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table";
  AddModUnionTable(mod_union_table);
  if (collector::SemiSpace::kUseRememberedSet && non_moving_space_ != main_space_) {
    accounting::RememberedSet* non_moving_space_rem_set =
        new accounting::RememberedSet("Non-moving space remembered set", this, non_moving_space_);
    CHECK(non_moving_space_rem_set != nullptr) << "Failed to create non-moving space remembered set";
    AddRememberedSet(non_moving_space_rem_set);
  }
  // TODO: Count objects in the image space here?
  num_bytes_allocated_.StoreRelaxed(0);
  mark_stack_.reset(accounting::ObjectStack::Create("mark stack", kDefaultMarkStackSize,
                                                    kDefaultMarkStackSize));
  const size_t alloc_stack_capacity = max_allocation_stack_size_ + kAllocationStackReserveSize;
  allocation_stack_.reset(accounting::ObjectStack::Create(
      "allocation stack", max_allocation_stack_size_, alloc_stack_capacity));
  live_stack_.reset(accounting::ObjectStack::Create(
      "live stack", max_allocation_stack_size_, alloc_stack_capacity));
  // It's still too early to take a lock because there are no threads yet, but we can create locks
  // now. We don't create it earlier to make it clear that you can't use locks during heap
  // initialization.
  gc_complete_lock_ = new Mutex("GC complete lock");
  gc_complete_cond_.reset(new ConditionVariable("GC complete condition variable",
                                                *gc_complete_lock_));
  heap_trim_request_lock_ = new Mutex("Heap trim request lock");
  last_gc_size_ = GetBytesAllocated();
  if (ignore_max_footprint_) {
    SetIdealFootprint(std::numeric_limits<size_t>::max());
    concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
  }
  CHECK_NE(max_allowed_footprint_, 0U);
  // Create our garbage collectors.
  for (size_t i = 0; i < 2; ++i) {
    const bool concurrent = i != 0;
    garbage_collectors_.push_back(new collector::MarkSweep(this, concurrent));
    garbage_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent));
    garbage_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent));
  }
  if (kMovingCollector) {
    // TODO: Clean this up.
    const bool generational = foreground_collector_type_ == kCollectorTypeGSS;
    semi_space_collector_ = new collector::SemiSpace(this, generational,
                                                     generational ? "generational" : "");
    garbage_collectors_.push_back(semi_space_collector_);
    concurrent_copying_collector_ = new collector::ConcurrentCopying(this);
    garbage_collectors_.push_back(concurrent_copying_collector_);
    mark_compact_collector_ = new collector::MarkCompact(this);
    garbage_collectors_.push_back(mark_compact_collector_);
  }
  if (GetImageSpace() != nullptr && non_moving_space_ != nullptr) {
    // Check that there's no gap between the image space and the non moving space so that the
    // immune region won't break (eg. due to a large object allocated in the gap).
    bool no_gap = MemMap::CheckNoGaps(GetImageSpace()->GetMemMap(),
                                      non_moving_space_->GetMemMap());
    if (!no_gap) {
      MemMap::DumpMaps(LOG(ERROR));
      LOG(FATAL) << "There's a gap between the image space and the main space";
    }
  }
  if (running_on_valgrind_) {
    Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
  }
  if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
    LOG(INFO) << "Heap() exiting";
  }
}

这个函数中,通过ChangeCollector和ChangeAllocator分别设置gc回收的策略和内存分配的策略,而且会根据不同的情况去选择不同的回收机制,至于gc的策略,比较复杂,后面具体用到了再来分析。
接下来继续分析,image_file_name此时为/system/framework/boot.art,非空而后调用ImageSpace::Create去创建一个ImageSpace对象,这个函数的分析可以参考“patchoat进程分析一文”,这里只是简要介绍,此时我们的进程是zygote进程,在调用ImageSpace::Create时候,可以假设系统是第一次运行,此时在/data/dalvik-cache/x86/目录下是没有缓存文件的,这个时候,zygote进程会fork一个子进程出来,也就是patchoat进程,同时传入一个随机地址delta,在patchoat进程中,会将/system/framework/x86下的boot.art和boot.oat文件进行重定向,以保证系统的安全性,接着再将这两个文件拷贝一份放至/data/dalvik-cache/x86/目录下,也就是所谓的art的缓存文件,最后patchoat进程退出,同时zygote进程被唤醒,接下来继续调用ImageSpace::Init创建一个ImageSpace对象和初始化一些信息,对于这个函数,在“patchoat进程分析一文”中也有分析,这里还是简单说明一下,Init函数先是调用OpenFileForReading打开这个文件,即是/data/dalvik-cache/x86/boot.art,file是指向这个文件的文件指针,将这个文件的头信息读入image_header中,获取到这个文件头信息之后,检查这个文件是否是有效的art文件,校验通过之后,接着调用MemMap::MapFileAtAddress去映射到内存,MapFileAtAddress函数中,做了一些页对齐的设置,而后就是mmap的东西。在这里,一共映射了两块内存区域。
map: 映射整个boot.art文件到内存,起始地址是经过重定向的
image_map: 映射了boot.art文件中的Bitmap的内容到内存,起始地址由系统决定
然后通过这两个映射区的指针,创建一个ImageSpace对象,同时打开对应的oat文件,保存在OatFile类型指针的成员变量oat_file_中,然后再设置了一些Runtime参数信息。
在继续分析之前,我们先来看这个函数

void Heap::AddSpace(space::Space* space) {
  CHECK(space != nullptr);
  WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
  if (space->IsContinuousSpace()) {
    DCHECK(!space->IsDiscontinuousSpace());
    space::ContinuousSpace* continuous_space = space->AsContinuousSpace();
    // Continuous spaces don't necessarily have bitmaps.
    accounting::ContinuousSpaceBitmap* live_bitmap = continuous_space->GetLiveBitmap();
    accounting::ContinuousSpaceBitmap* mark_bitmap = continuous_space->GetMarkBitmap();
    if (live_bitmap != nullptr) {
      CHECK(mark_bitmap != nullptr);
      live_bitmap_->AddContinuousSpaceBitmap(live_bitmap);
      mark_bitmap_->AddContinuousSpaceBitmap(mark_bitmap);
    }
    continuous_spaces_.push_back(continuous_space);
    // Ensure that spaces remain sorted in increasing order of start address.
    std::sort(continuous_spaces_.begin(), continuous_spaces_.end(),
              [](const space::ContinuousSpace* a, const space::ContinuousSpace* b) {
      return a->Begin() < b->Begin();
    });
  } else {
    CHECK(space->IsDiscontinuousSpace());
    space::DiscontinuousSpace* discontinuous_space = space->AsDiscontinuousSpace();
    live_bitmap_->AddLargeObjectBitmap(discontinuous_space->GetLiveBitmap());
    mark_bitmap_->AddLargeObjectBitmap(discontinuous_space->GetMarkBitmap());
    discontinuous_spaces_.push_back(discontinuous_space);
  }
  if (space->IsAllocSpace()) {
    alloc_spaces_.push_back(space->AsAllocSpace());
  }
}

AddSpace函数涉及到一些空间的管理,空间连续空间和非连续空间,Heap类中分别用成员变量continuous_spaces_和discontinuous_spaces_分别保存整个堆中的连续空间和非连续空间,对于Space来说,又可分为可分配空间和不可分配空间,比如ImageSpace就是一种不可分配空间,它存放的是镜像art文件,如果是可分配,则存放至alloc_spaces_成员变量中,进行管理。
回到Heap的构造函数,接下来,将这个创建好的ImageSpace对象,调用函数AddSpace让heap可以对它进行管理,接着调整Heap区的requested_alloc_space_begin指针,保证后续分配的空间在这个ImageSpace的后面。下面是一个空间分配图

 requested_alloc_space_begin ->     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
                                     +-  nonmoving space (non_moving_space_capacity)+-
                                     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
                                     +-????????????????????????????????????????????+-
                                     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
                                     +-main alloc space / bump space 1 (capacity_) +-
                                     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
                                     +-????????????????????????????????????????????+-
                                     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
                                     +-main alloc space2 / bump space 2 (capacity_)+-
                                     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

继续分析这个函数,代码比较长,对于separate_non_moving_space这个域,因为是在zygote进程,所以还需要紧接在requested_alloc_space_begin分配一个64MB的Zygote Space,接下来继续分配两块main_space,这里默认值是256MB,名字分别为"main space", "main space 1",ART运行时支持的各种GC的堆空间结构,不同的GC堆空间也有所区别,函数中然后将各种Space添加到Heap中去,以便能管理起来,总体这个类里面涉及的内容较多和很抽象,分析了这么多,其实这个就是Java内存管理上很重要的一部分,涉及了java的自动GC机制,Java层堆内存的分配,均要和此类打交道,我们这里暂且不太深入去了解,这部分需要详细介绍。

Step10: JavaVmExt::JavaVmExt (/art/runtime/jni_internal.cc)###

JavaVMExt::JavaVMExt(Runtime* runtime, ParsedOptions* options)
    : runtime(runtime),
      check_jni_abort_hook(nullptr),
      check_jni_abort_hook_data(nullptr),
      check_jni(false),
      force_copy(false),  // TODO: add a way to enable this
      trace(options->jni_trace_),
      globals_lock("JNI global reference table lock"),
      globals(gGlobalsInitial, gGlobalsMax, kGlobal),
      libraries_lock("JNI shared libraries map lock", kLoadLibraryLock),
      libraries(new Libraries),
      weak_globals_lock_("JNI weak global reference table lock"),
      weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
      allow_new_weak_globals_(true),
      weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
  functions = unchecked_functions = &gJniInvokeInterface;
  if (options->check_jni_) {
    SetCheckJniEnabled(true);
  }
}

JavaVmExt继承自JavaVM,通过其构造函数,传入Runtime实例,完成了一些成员变量的初始化,并且也完成了成员变量functions的结构指针的初始化了,上文讲到JavaVM是进程相关,而JNIEnv是线程相关,这里初始化了这个线程共享的Globals ref全局变量。后续所有涉及到的JavaVM一系列的方法,实际上是由这个functions指针所指向的函数地址确定的,类似一个委托模式,后面会看到。

Step11: Thread::Startup (/art/runtime/thread.cc)###

void Thread::Startup() {
  CHECK(!is_started_);
  is_started_ = true;
  {
    // MutexLock to keep annotalysis happy.
    //
    // Note we use nullptr for the thread because Thread::Current can
    // return garbage since (is_started_ == true) and
    // Thread::pthread_key_self_ is not yet initialized.
    // This was seen on glibc.
    MutexLock mu(nullptr, *Locks::thread_suspend_count_lock_);
    resume_cond_ = new ConditionVariable("Thread resumption condition variable",
                                         *Locks::thread_suspend_count_lock_);
  }

  // Allocate a TLS slot.
  CHECK_PTHREAD_CALL(pthread_key_create, (&Thread::pthread_key_self_, Thread::ThreadExitCallback), "self key");

  // Double-check the TLS slot allocation.
  if (pthread_getspecific(pthread_key_self_) != nullptr) {
    LOG(FATAL) << "Newly-created pthread TLS slot is not nullptr";
  }
}

Thread类是art虚拟机管理线程的一个类,里面不仅有由jvm直接创建的线程,还有本地native创建的线程,实际上,每一个jvm线程和一个native线程都是一一对应的,jvm的线程调度机制并没有自己实现,而是借助操作系统的调度,这个Thread类就是协助管理这些线程对象。在这个静态函数中,将类标志位is_started_置为true,接下来代码,用到TLS机制,TLS是什么呢,如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量(被称为static memory local to a thread 线程局部静态变量),就需要新的机制来实现,这就是TLS机制,在不同的线程中pthread_getspecific将获取到不同的值。

Step12: Thread::Attach (/art/runtime/thread.cc)###

// Attaches the calling native thread to the runtime, returning the new native peer.
// Used to implement JNI AttachCurrentThread and AttachCurrentThreadAsDaemon calls.
Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_group,
                       bool create_peer) {
  Thread* self;
  Runtime* runtime = Runtime::Current();
  if (runtime == nullptr) {
    LOG(ERROR) << "Thread attaching to non-existent runtime: " << thread_name;
    return nullptr;
  }
  {
    MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_);
    if (runtime->IsShuttingDownLocked()) {
      LOG(ERROR) << "Thread attaching while runtime is shutting down: " << thread_name;
      return nullptr;
    } else {
      Runtime::Current()->StartThreadBirth();
      self = new Thread(as_daemon);
      self->Init(runtime->GetThreadList(), runtime->GetJavaVM());
      Runtime::Current()->EndThreadBirth();
    }
  }

  CHECK_NE(self->GetState(), kRunnable);
  self->SetState(kNative);

  // If we're the main thread, ClassLinker won't be created until after we're attached,
  // so that thread needs a two-stage attach. Regular threads don't need this hack.
  // In the compiler, all threads need this hack, because no-one's going to be getting
  // a native peer!
  if (create_peer) {
    self->CreatePeer(thread_name, as_daemon, thread_group);
  } else {
    // These aren't necessary, but they improve diagnostics for unit tests & command-line tools.
    if (thread_name != nullptr) {
      self->tlsPtr_.name->assign(thread_name);
      ::art::SetThreadName(thread_name);
    } else if (self->GetJniEnv()->check_jni) {
      LOG(WARNING) << *Thread::Current() << " attached without supplying a name";
    }
  }

  {
    ScopedObjectAccess soa(self);
    Dbg::PostThreadStart(self);
  }

  return self;
}

函数的注释其实写的比较清楚了,就是将本地native线程和Runtime关联起来,从而jvm也能去管理它,函数首先是拿到当前的Runtime实例,进行一些非空检查,接下来创建一个Thread的实例,并调用其init函数,传入当前Runtime中的ThreadList和JavaVMExt指针,这里ThreadList顾名思义就是用来存放Thread的list。

Step13: Thread::Init (/art/runtime/thread.cc)###

void Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) {
  // This function does all the initialization that must be run by the native thread it applies to.
  // (When we create a new thread from managed code, we allocate the Thread* in Thread::Create so
  // we can handshake with the corresponding native thread when it's ready.) Check this native
  // thread hasn't been through here already...
  CHECK(Thread::Current() == nullptr);
  SetUpAlternateSignalStack();
  InitCpu();
  InitTlsEntryPoints();
  RemoveSuspendTrigger();
  InitCardTable();
  InitTid();
  // Set pthread_self_ ahead of pthread_setspecific, that makes Thread::Current function, this
  // avoids pthread_self_ ever being invalid when discovered from Thread::Current().
  tlsPtr_.pthread_self = pthread_self();
  CHECK(is_started_);
  CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, this), "attach self");
  DCHECK_EQ(Thread::Current(), this);

  tls32_.thin_lock_thread_id = thread_list->AllocThreadId(this);
  InitStackHwm();

  tlsPtr_.jni_env = new JNIEnvExt(this, java_vm);
  thread_list->Register(this);
}

这个函数做了所有的初始化工作,必须由对应的本地线程来执行,这部分代码很复杂,涉及到操作系统的一些知识,各个架构x86 arm均有不同的实现,下面以主流的arm为例,简单分析下,主要有设置信号堆栈,初始化Cpu信息,成员函数InitTlsEntryPoints初始化一个外部库函数调用跳转表,Thread类将外部库函数调用跳转表划分为4个,其中,interpreter_entrypoints_描述的是解释器要用到的跳转表,jni_entrypoints_描述的是JNI调用相关的跳转表,portable_entrypoints_描述的是Portable后端生成的本地机器指令要用到的跳转表,而quick_entrypoints_描述的是Quick后端生成的本地机器指令要用到的跳转表。接下来再设置一些thread栈信息,创建当前线程的JNIEnvExt对象,最后设置完成之后将当前Thread对象添加到运行时ThreadList中,进行管理。我们先分析下InitTlsEntryPoints的具体实现。

void Thread::InitTlsEntryPoints() {
  // Insert a placeholder so we can easily tell if we call an unimplemented entry point.
  uintptr_t* begin = reinterpret_cast<uintptr_t*>(&tlsPtr_.interpreter_entrypoints);
  uintptr_t* end = reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(begin) +
                                                sizeof(tlsPtr_.quick_entrypoints));
  for (uintptr_t* it = begin; it != end; ++it) {
    *it = reinterpret_cast<uintptr_t>(UnimplementedEntryPoint);
  }
  InitEntryPoints(&tlsPtr_.interpreter_entrypoints, &tlsPtr_.jni_entrypoints,
                  &tlsPtr_.portable_entrypoints, &tlsPtr_.quick_entrypoints);
}

它本身的实现很简单,继续调用函数InitEntryPoints去初始化这4个外部函数调用跳转表。这里以arm体系为例子。

Step14: InitEntryPoints (/art/runtime/arch/arm/entrypoints_init_arm.cc)###

void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
                     PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
  // Interpreter
  ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
  ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;

  // JNI
  jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;

  // Portable
  ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
  ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;

  // Alloc
  ResetQuickAllocEntryPoints(qpoints);

  // Cast
  qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
  qpoints->pCheckCast = art_quick_check_cast;

  // DexCache
  qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
  qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
  qpoints->pInitializeType = art_quick_initialize_type;
  qpoints->pResolveString = art_quick_resolve_string;

  // Field
  qpoints->pSet32Instance = art_quick_set32_instance;
  qpoints->pSet32Static = art_quick_set32_static;
  qpoints->pSet64Instance = art_quick_set64_instance;
  qpoints->pSet64Static = art_quick_set64_static;
  qpoints->pSetObjInstance = art_quick_set_obj_instance;
  qpoints->pSetObjStatic = art_quick_set_obj_static;
  qpoints->pGet32Instance = art_quick_get32_instance;
  qpoints->pGet64Instance = art_quick_get64_instance;
  qpoints->pGetObjInstance = art_quick_get_obj_instance;
  qpoints->pGet32Static = art_quick_get32_static;
  qpoints->pGet64Static = art_quick_get64_static;
  qpoints->pGetObjStatic = art_quick_get_obj_static;

  // Array
  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
  qpoints->pAputObject = art_quick_aput_obj;
  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;

  // JNI
  qpoints->pJniMethodStart = JniMethodStart;
  qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
  qpoints->pJniMethodEnd = JniMethodEnd;
  qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
  qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
  qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;

  // Locks
  qpoints->pLockObject = art_quick_lock_object;
  qpoints->pUnlockObject = art_quick_unlock_object;

  // Math
  qpoints->pCmpgDouble = CmpgDouble;
  qpoints->pCmpgFloat = CmpgFloat;
  qpoints->pCmplDouble = CmplDouble;
  qpoints->pCmplFloat = CmplFloat;
  qpoints->pFmod = fmod;
  qpoints->pL2d = __aeabi_l2d;
  qpoints->pFmodf = fmodf;
  qpoints->pL2f = __aeabi_l2f;
  qpoints->pD2iz = __aeabi_d2iz;
  qpoints->pF2iz = __aeabi_f2iz;
  qpoints->pIdivmod = __aeabi_idivmod;
  qpoints->pD2l = art_d2l;
  qpoints->pF2l = art_f2l;
  qpoints->pLdiv = __aeabi_ldivmod;
  qpoints->pLmod = __aeabi_ldivmod;  // result returned in r2:r3
  qpoints->pLmul = art_quick_mul_long;
  qpoints->pShlLong = art_quick_shl_long;
  qpoints->pShrLong = art_quick_shr_long;
  qpoints->pUshrLong = art_quick_ushr_long;

  // Intrinsics
  qpoints->pIndexOf = art_quick_indexof;
  qpoints->pStringCompareTo = art_quick_string_compareto;
  qpoints->pMemcpy = memcpy;

  // Invocation
  qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
  qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
  qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
  qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
  qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
  qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check;
  qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check;
  qpoints->pInvokeVirtualTrampolineWithAccessCheck = art_quick_invoke_virtual_trampoline_with_access_check;

  // Thread
  qpoints->pTestSuspend = art_quick_test_suspend;

  // Throws
  qpoints->pDeliverException = art_quick_deliver_exception;
  qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
  qpoints->pThrowDivZero = art_quick_throw_div_zero;
  qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
  qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
  qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
};

这个函数设置了一些跳转函数表,包括解释器,jni,quick内存分配等等一系列的跳转表信息,这些都是保存在当前线程的成员变量之中,我们这里研究的是启动过程,至于JVM的本地执行和解释执行这两种方式的调配,不是我们的主题。接下来函数返回,继续执行Thread的Init函数的后续操作,比较重要的是,创建了JniEnv的实例,前面提到,它是线程相关的。

Step15: JNIEnvExt::JNIEnvExt ()###

JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm)
    : self(self),
      vm(vm),
      local_ref_cookie(IRT_FIRST_SEGMENT),
      locals(kLocalsInitial, kLocalsMax, kLocal),
      check_jni(false),
      critical(0),
      monitors("monitors", kMonitorsInitial, kMonitorsMax) {
  functions = unchecked_functions = &gJniNativeInterface;
  if (vm->check_jni) {
    SetCheckJniEnabled(true);
  }
}

这个构造函数也比较简单,将JavaVMExt和Thread关联起来,初始化自己的Local ref的相关信息,这个是线程内部的,同时将其成员变量functions赋值,至此以后JNIENV一系列的方法都将由这个functions成员指针所指向的函数指针来进行跳转。而后层层返回,回到Runtime::Init函数中,接着加载了class_linker和LoadNativeBridge这些工作,class_linker主要是解析一些系统java类,比如classloader等系统资源load到内存,作为之后加载其他java类的基础,这里只是简单说明下,涉及到art的几个很重要的结构,Class和ArtMethod,DexCache,在boot.oat可执行文件的结构中,每一个Java Class对应一个OatClass,OatClass中保存分别有dex class和本地机器码的地址,在解释执行模式下,每一个方法的调用是通过DexCache来中转的,DexCache,根据method找到指定的ArtMethod,这里class_link主要负责协调管理。另外LoadNativeBridge,主要是为架构不兼容做了一个桥接。最后层层退出,接下来回来继续分析JNI_CreateJavaVM这个函数。在JNI_CreateJavaVM函数中继续调用Runtime::Start。

Step16: Runtime::Start()

bool Runtime::Start() {
  VLOG(startup) << "Runtime::Start entering";

  // Restore main thread state to kNative as expected by native code.
  Thread* self = Thread::Current();

  self->TransitionFromRunnableToSuspended(kNative);

  started_ = true;

  if (IsZygote()) {
    ScopedObjectAccess soa(self);
    gc::space::ImageSpace* image_space = heap_->GetImageSpace();
    if (image_space != nullptr) {
      Runtime::Current()->GetInternTable()->AddImageStringsToTable(image_space);
      Runtime::Current()->GetClassLinker()->MoveImageClassesToClassTable();
    }
  }

  if (!IsImageDex2OatEnabled() || !Runtime::Current()->GetHeap()->HasImageSpace()) {
    ScopedObjectAccess soa(self);
    StackHandleScope<1> hs(soa.Self());
    auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass()));
    class_linker_->EnsureInitialized(klass, true, true);
  }

  // InitNativeMethods needs to be after started_ so that the classes
  // it touches will have methods linked to the oat file if necessary.
  InitNativeMethods();

  // Initialize well known thread group values that may be accessed threads while attaching.
  InitThreadGroups(self);

  Thread::FinishStartup();

  system_class_loader_ = CreateSystemClassLoader();

  if (is_zygote_) {
    if (!InitZygote()) {
      return false;
    }
  } else {
    if (is_native_bridge_loaded_) {
      PreInitializeNativeBridge(".");
    }
    DidForkFromZygote(self->GetJniEnv(), NativeBridgeAction::kInitialize,
                      GetInstructionSetString(kRuntimeISA));
  }

  StartDaemonThreads();

  {
    ScopedObjectAccess soa(self);
    self->GetJniEnv()->locals.AssertEmpty();
  }

  VLOG(startup) << "Runtime::Start exiting";
  finished_starting_ = true;

  if (profiler_options_.IsEnabled() && !profile_output_filename_.empty()) {
    // User has asked for a profile using -Xenable-profiler.
    // Create the profile file if it doesn't exist.
    int fd = open(profile_output_filename_.c_str(), O_RDWR|O_CREAT|O_EXCL, 0660);
    if (fd >= 0) {
      close(fd);
    } else if (errno != EEXIST) {
      LOG(INFO) << "Failed to access the profile file. Profiler disabled.";
      return true;
    }
    StartProfiler(profile_output_filename_.c_str());
  }
  return true;
}

这个函数中,首先将静态成员变量started_置为ture,表示已经启动。这里我们启动的是Zygote进程,两个比较重要的函数是 InitNativeMethods和InitThreadGroups,InitNativeMethods函数中,进行了jni和java class的一些初始化工作,同时也注册了一些系统需要的jni本地方法,而InitThreadGroups则是创建了线程组的信,具体的这里就不展开了。

Step18: AndroidRuntime::startReg

/*
 * Register android native functions with the VM.
 */
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    /*
     * This hook causes all future threads created in this process to be
     * attached to the JavaVM.  (This needs to go away in favor of JNI
     * Attach calls.)
     */
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

    ALOGV("--- registering native functions ---\n");
    /*
     * Every "register" function calls one or more things that return
     * a local reference (e.g. FindClass).  Because we haven't really
     * started the VM yet, they're all getting stored in the base frame
     * and never released.  Use Push/Pop to manage the storage.
     */
    env->PushLocalFrame(200);

    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);

    //createJavaThread("fubar", quickTest, (void*) "hello");

    return 0;
}

函数的说明已经很明确了,在上文Runtime和Thread以及class_link准备工作都完成之后,这里就开始注册android系统依赖的本地jni函数。

Step19: CallStaticVoidMethod zygote###

  char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }

最后继续分析start方法,这里的className就是“com.android.internal.os.ZygoteInit”,一目了然,接下来就是调用ZygoteInit的main函数,开启Java世界。整个art虚拟机部分其实十分复杂,虚拟机其实就是一个C++程序,只是这个程序里面自定义了一套解释执行的规则,以及内部封装了一些动态内存的GC机制,本质上,其实还是一个C++的程序。软件发展到今天,其实也就是一步步抽象上来的,从原始的机器码,到汇编程序,再到C,最后发展出来各种高级语言如Java、C++等,本质始终没有变化。本文分析的比较粗糙,一来知识水平有限,希望能给大家一个宏观的认知。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容