標簽(空格分隔): art android5.1 啟動流程 jvm
我們都已經知道,Android系統(tǒng)是基于Linux內核,而應用程序大都由Java語言實現(xiàn)的一個操作系統(tǒng),包含一套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,它在一個腳本文件中被系統(tǒng)創(chuàng)建出來,如下
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系統(tǒng)下,app_process是一個bin類型的可執(zhí)行文件,它的源碼在/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程序運行時環(huán)境
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;
}
}
代碼先是創(chuàng)建了一個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",繼續(xù)解析命令行參數,while循環(huán)完成后,此時zygote和startSystemServer為true niceName為“zygote”,表示當前是在zygote模式下,接下來調用maybeCreateDalvikCache方法,創(chuàng)建這個/data/dalvik-cache/x86文件(我這里是x86的架構,還有arm等),存放字節(jié)碼的緩存文件。接下來設置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清除這塊內存區(qū),同時將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方法實現(xiàn)如下:
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");
}
這個方法控制了整個啟動的流程,主要由以下幾步:
->創(chuàng)建JniInvocation,加載art虛擬機so文件(libart.so)。
->調用函數startVM,創(chuàng)建JavaVM和JNIEnv
->注冊Android jni方法
->啟動到Java世界
下面分段閱讀這幾個過程。
Step3: JniInvocation::Init (/libnativehelper/JniInvocation.cpp)###
該函數中首先獲取要加載的library名字,默認是libart.so庫文件,并打開這個動態(tài)庫并解析出所有未定義符號,同時返回動態(tài)鏈接庫的句柄給handle_指針變量。接下來繼續(xù)解析,將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;
}
函數執(zhí)行完畢之后,返回至AndroidRuntime的start,將控制權歸還,接著執(zhí)行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獲取系統(tǒng)屬性文件,設置大量關于虛擬機的一些參數信息,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;
繼續(xù)往下,接著調用JNI_CreateJavaVM去進一步創(chuàng)建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的一個友元函數,在它里面繼續(xù)調用JniInvocation類的JNI_CreateJavaVM方法,在這個方法中,通過函數指針JNI_CreateJavaVM_去調用libart.so動態(tài)庫中的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版本是否合法。接著創(chuàng)建RuntimeOptions的options對象,去保存前面存儲在JavaVMInitArgs options的參數,options中存放的是前面類AndroidRuntime成員變量Vector<JavaVMOption> mOptions這個數組的首地址。因此代碼中for循環(huán)一次去取這些參數信息,并push到當前RuntimeOptions的options對象中,RuntimeOptions存放的元素pair實質上是一個結構體,其主要的兩個成員變量是first和second就是一個鍵值對的存放形式,主要的作用是將兩個數據組合成一個數據。其定義為
typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions;
參數全部存放至options后,接著開始著手創(chuàng)建Runtime對象,并進行一些初始工作,它描述了JavaVM運行時的所有的信息,也即是我們本文的另一個主角。函數會去調用Runtime::Create和Runtime::Start()去完成這些工作。
Step7: Runtime::Create (/art/runtime/runtime.cc)###
函數先是初始化log工具,而后創(chuàng)建一個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
多鍵映射表容器是一個按特定順序存儲以鍵值對組合而成的元素的關聯(lián)容器,容器中元素的主鍵可以相等,
Key : 主鍵的類型
T : 被映射的值的類型
Compare :一個二元謂詞,以兩個元素的主鍵為參數返回一個 bool 值。可以是函數指針類型或函數對象類型
Alloc : 容器內部用來管理內存分配及釋放的內存分配器的類型。這個參數是可選的,它的默認值是 std::allocator<T>
std::less : 用做比較,等價于 x < y
std::pair : 實現(xiàn)了二元組
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的模板有些區(qū)別,從這里可以知道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系統(tǒng)必須的的jar包編譯生成的,同時生成的boot.oat文件可以將其理解為ART虛擬機的啟動類。
image_instruction_set : 是要運行的應用程序所支持的指令集,而kRuntimeISA是指當前Android系統(tǒng)所運行平臺的指令集,它是在編譯時就指定好了的,主要由kArm、kArm64、kMips、kX86、kX86_64,接下來根據相關參數信息創(chuàng)建一個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的策略,比較復雜,后面具體用到了再來分析。
接下來繼續(xù)分析,image_file_name此時為/system/framework/boot.art,非空而后調用ImageSpace::Create去創(chuàng)建一個ImageSpace對象,這個函數的分析可以參考“patchoat進程分析一文”,這里只是簡要介紹,此時我們的進程是zygote進程,在調用ImageSpace::Create時候,可以假設系統(tǒng)是第一次運行,此時在/data/dalvik-cache/x86/目錄下是沒有緩存文件的,這個時候,zygote進程會fork一個子進程出來,也就是patchoat進程,同時傳入一個隨機地址delta,在patchoat進程中,會將/system/framework/x86下的boot.art和boot.oat文件進行重定向,以保證系統(tǒng)的安全性,接著再將這兩個文件拷貝一份放至/data/dalvik-cache/x86/目錄下,也就是所謂的art的緩存文件,最后patchoat進程退出,同時zygote進程被喚醒,接下來繼續(xù)調用ImageSpace::Init創(chuàng)建一個ImageSpace對象和初始化一些信息,對于這個函數,在“patchoat進程分析一文”中也有分析,這里還是簡單說明一下,Init函數先是調用OpenFileForReading打開這個文件,即是/data/dalvik-cache/x86/boot.art,file是指向這個文件的文件指針,將這個文件的頭信息讀入image_header中,獲取到這個文件頭信息之后,檢查這個文件是否是有效的art文件,校驗通過之后,接著調用MemMap::MapFileAtAddress去映射到內存,MapFileAtAddress函數中,做了一些頁對齊的設置,而后就是mmap的東西。在這里,一共映射了兩塊內存區(qū)域。
map: 映射整個boot.art文件到內存,起始地址是經過重定向的
image_map: 映射了boot.art文件中的Bitmap的內容到內存,起始地址由系統(tǒng)決定
然后通過這兩個映射區(qū)的指針,創(chuàng)建一個ImageSpace對象,同時打開對應的oat文件,保存在OatFile類型指針的成員變量oat_file_中,然后再設置了一些Runtime參數信息。
在繼續(xù)分析之前,我們先來看這個函數
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函數涉及到一些空間的管理,空間連續(xù)空間和非連續(xù)空間,Heap類中分別用成員變量continuous_spaces_和discontinuous_spaces_分別保存整個堆中的連續(xù)空間和非連續(xù)空間,對于Space來說,又可分為可分配空間和不可分配空間,比如ImageSpace就是一種不可分配空間,它存放的是鏡像art文件,如果是可分配,則存放至alloc_spaces_成員變量中,進行管理。
回到Heap的構造函數,接下來,將這個創(chuàng)建好的ImageSpace對象,調用函數AddSpace讓heap可以對它進行管理,接著調整Heap區(qū)的requested_alloc_space_begin指針,保證后續(xù)分配的空間在這個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_)+-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
繼續(xù)分析這個函數,代碼比較長,對于separate_non_moving_space這個域,因為是在zygote進程,所以還需要緊接在requested_alloc_space_begin分配一個64MB的Zygote Space,接下來繼續(xù)分配兩塊main_space,這里默認值是256MB,名字分別為"main space", "main space 1",ART運行時支持的各種GC的堆空間結構,不同的GC堆空間也有所區(qū)別,函數中然后將各種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全局變量。后續(xù)所有涉及到的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直接創(chuàng)建的線程,還有本地native創(chuàng)建的線程,實際上,每一個jvm線程和一個native線程都是一一對應的,jvm的線程調度機制并沒有自己實現(xiàn),而是借助操作系統(tǒng)的調度,這個Thread類就是協(xié)助管理這些線程對象。在這個靜態(tài)函數中,將類標志位is_started_置為true,接下來代碼,用到TLS機制,TLS是什么呢,如果需要在一個線程內部的各個函數調用都能訪問、但其它線程不能訪問的變量(被稱為static memory local to a thread 線程局部靜態(tài)變量),就需要新的機制來實現(xiàn),這就是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關聯(lián)起來,從而jvm也能去管理它,函數首先是拿到當前的Runtime實例,進行一些非空檢查,接下來創(chuàng)建一個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);
}
這個函數做了所有的初始化工作,必須由對應的本地線程來執(zhí)行,這部分代碼很復雜,涉及到操作系統(tǒng)的一些知識,各個架構x86 arm均有不同的實現(xiàn),下面以主流的arm為例,簡單分析下,主要有設置信號堆棧,初始化Cpu信息,成員函數InitTlsEntryPoints初始化一個外部庫函數調用跳轉表,Thread類將外部庫函數調用跳轉表劃分為4個,其中,interpreter_entrypoints_描述的是解釋器要用到的跳轉表,jni_entrypoints_描述的是JNI調用相關的跳轉表,portable_entrypoints_描述的是Portable后端生成的本地機器指令要用到的跳轉表,而quick_entrypoints_描述的是Quick后端生成的本地機器指令要用到的跳轉表。接下來再設置一些thread棧信息,創(chuàng)建當前線程的JNIEnvExt對象,最后設置完成之后將當前Thread對象添加到運行時ThreadList中,進行管理。我們先分析下InitTlsEntryPoints的具體實現(xiàn)。
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);
}
它本身的實現(xiàn)很簡單,繼續(xù)調用函數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的本地執(zhí)行和解釋執(zhí)行這兩種方式的調配,不是我們的主題。接下來函數返回,繼續(xù)執(zhí)行Thread的Init函數的后續(xù)操作,比較重要的是,創(chuàng)建了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關聯(lián)起來,初始化自己的Local ref的相關信息,這個是線程內部的,同時將其成員變量functions賦值,至此以后JNIENV一系列的方法都將由這個functions成員指針所指向的函數指針來進行跳轉。而后層層返回,回到Runtime::Init函數中,接著加載了class_linker和LoadNativeBridge這些工作,class_linker主要是解析一些系統(tǒng)java類,比如classloader等系統(tǒng)資源load到內存,作為之后加載其他java類的基礎,這里只是簡單說明下,涉及到art的幾個很重要的結構,Class和ArtMethod,DexCache,在boot.oat可執(zhí)行文件的結構中,每一個Java Class對應一個OatClass,OatClass中保存分別有dex class和本地機器碼的地址,在解釋執(zhí)行模式下,每一個方法的調用是通過DexCache來中轉的,DexCache,根據method找到指定的ArtMethod,這里class_link主要負責協(xié)調管理。另外LoadNativeBridge,主要是為架構不兼容做了一個橋接。最后層層退出,接下來回來繼續(xù)分析JNI_CreateJavaVM這個函數。在JNI_CreateJavaVM函數中繼續(xù)調用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;
}
這個函數中,首先將靜態(tài)成員變量started_置為ture,表示已經啟動。這里我們啟動的是Zygote進程,兩個比較重要的函數是 InitNativeMethods和InitThreadGroups,InitNativeMethods函數中,進行了jni和java class的一些初始化工作,同時也注冊了一些系統(tǒng)需要的jni本地方法,而InitThreadGroups則是創(chuàng)建了線程組的信,具體的這里就不展開了。
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系統(tǒng)依賴的本地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
}
}
最后繼續(xù)分析start方法,這里的className就是“com.android.internal.os.ZygoteInit”,一目了然,接下來就是調用ZygoteInit的main函數,開啟Java世界。整個art虛擬機部分其實十分復雜,虛擬機其實就是一個C++程序,只是這個程序里面自定義了一套解釋執(zhí)行的規(guī)則,以及內部封裝了一些動態(tài)內存的GC機制,本質上,其實還是一個C++的程序。軟件發(fā)展到今天,其實也就是一步步抽象上來的,從原始的機器碼,到匯編程序,再到C,最后發(fā)展出來各種高級語言如Java、C++等,本質始終沒有變化。本文分析的比較粗糙,一來知識水平有限,希望能給大家一個宏觀的認知。