精华内容
下载资源
问答
  • ART模式相信大家都比较陌生,不过ART模式越来越多的被媒体提及,很多网友对于ART模式是什么以及有什么用确认详细的了解。...ART模式英文全称为:Android runtime,谷歌Android 4.4系统新增的一种应用运行模式,与...

    ART模式相信大家都比较陌生,不过ART模式越来越多的被媒体提及,很多网友对于ART模式是什么以及有什么用确认详细的了解。以下脚本之家为大家详细汇总了ART模式相关知识汇总,通过本文,您可以全面了解ART模式是什么以及有什么用等。

    63a7baf7d8493c8d5c56f6b106b7edc6.png

    ART模式是什么 ART模式有什么用?

    一、ART模式是什么?

    ART模式英文全称为:Android runtime,谷歌Android 4.4系统新增的一种应用运行模式,与传统的Dalvik模式不同,ART模式可以实现更为流畅的安卓系统体验,对于大家来说,只要明白ART模式可让系统体验更加流畅,不过只有在安卓4.4以上系统中采用此功能。

    如果你对ART模式原理感兴趣,不放接下来往下看。

    在4.4系统之前,Android系统在Linux的底层下构筑Dalvik一层的虚拟机,通过其可以更好适应多样的硬件架构,开发者只需要按一套规则进行应用便可,无需因为不同的硬件架构而处理与底层的驱动关系,大大提高开发的效率。

    712dec502cea5aa925862cb0d1cb2f12.png

    ART模式原理图

    不过提高了开发效率,但因为应用均是运行在Dalvik虚拟机中,因此应用程序每次运行的时候,一部分代码都需要重新进行编译,这过程需要消耗一定的时间和降低应用的执行效率,最明显的便是拖延了应用的启动时间和降低运行速度。谷歌在安卓4.4之前的系统中加多一层虚拟机的初衷,不但是为了加快应用的开发效率,更是为了适应Android诞生之初硬件多样性的问题。

    不过最新的Android 4.4,谷歌准备利用新的模式去代替之前Dalvik虚拟机,那便是ART模式。ART模式便是Android Runtime,其能够令系统更加高效和省电,执行效率大大提高。

    二、ART模式有什么用?

    通过以上详细的ART模式介绍,相信大家对于ART模式的作用已经有所了解了,ART模式最大的作用就是提升了安卓系统流畅度,相比Dalvik模式中出现的耗电快、占用内存大、即使是旗舰机用久了也会卡顿严重等现象,ART模式中这种问题得到了很好的解决,通过在安装应用程序时,自动对程序进行代码预读取编译,让程序直接编译成机器语言,免去了Dalvik模式要时时转换代码,实现高效率、省电、占用更低的系统内存、手机运行流畅。

    之前谷歌宣称,安卓4.4运行内存仅有512M RAM即可流畅运行,也就是所,一些老款低配机也可以流畅运行Android 4.4系统,从侧面也可以看出ART模式对于提升系统流畅度还是非常有帮助的。

    三、ART模式其他问题

    任务事物都有两面性,ART模式可以降低手机硬件配置要求,减少RAM内存依赖,不过在安卓4.4系统中,安装应用的时间比安卓4.4以下版本系统更长,这主要由于应用安装过程中需要先执行编码导致,并且安装应用更占存储空间(ROM)。

    3bca33d8dba251d6be00b34f6b9383da.png

    ART模式更占ROM存储空间

    根据目前的测试,ART模式安装应用所占的ROM空间比Dalvik模式多了接近一倍,不过鉴于目前手机ROM存储空间越来越大,并且支持扩展,这些都不是什么问题。

    另外值得一提的是,ART模式目前还存在一些兼容性问题,绝大多数硬件均可以正常运行运行,但也有部分应用会不兼容,会出现闪退现象。这主要是由于Android 4.4并未大规模普及,后期会逐渐得到普及优化。

    0d333db1bb9bde3aa3e05085fb42a07e.png

    ART模式兼容性还有待普及

    文至于此,相信大家对于ART模式是什么已经有一个深度了解了,作为安卓4.4系统的一项模式,用户可以在安卓4.4系统的手机中自由切换传统Dalvik模式和ART模式,大大方便用户使用,概括的是ART模式可以大大提升安卓系统的流畅性。

    展开全文
  • Android ART机制分析

    2021-06-02 16:04:39
    文章链接:外链网址已屏蔽作者:Jack_Jia邮箱:2013年度“博客之星”投票火热进行中,欢迎投票支持我:一、Android系统性能提升之路Dalvik虚拟机作为Android平台的核心组成部分之一,允许在有限的内存资源中同时运行...

    本文章由Jack_Jia编写,转载请注明出处。文章链接:外链网址已屏蔽

    作者:Jack_Jia邮箱:

    2013年度“博客之星”投票火热进行中,欢迎投票支持我:

    一、Android系统性能提升之路

    Dalvik虚拟机作为Android平台的核心组成部分之一,允许在有限的内存资源中同时运行多个虚拟机实例。Dalvik虚拟机通过以下方式提升性能:

    1、DEX代码安装时或第一次动态加载时odex化处理。

    2、Android2.2版本提供了JIT机制提升性能,号称性能提升3~5倍。

    3、提升硬件配置,如更多核CPU、更高频率CPU、更大的RAM等。

    但是Android的系统流畅度与IOS系统还是有一定得差距。Android代码必须运行在Dalvik虚拟机上,而IOS直接是本地代码,性能差距也在情理之中。如果Android系统想拥有与IOS系统相同的系统性能。Dalvik虚拟机运行机制就成为Android系统性能提升唯一的障碍。

    Android Kitkat 提供了一种与Dalvik截然不同的运行环境-ART(Android Runtime)的支持。目前用户可以选择设备的运行环境,在不久的将来ART肯定会替代Dalvik Runtime。

    二、Dalvik vs ART

    Dalvik运行环境使用JIT(Just-In-Time)来进行转译,应用每次运行的时候,字节码都需要通过JIT转换为机器码,这会拖慢应用的运行效率。而ART则是使用AOT进行处理(Ahead-Of-Time),并会在应用程序安装完毕时,进行预先的基础性编译作业,这就减去了JIT运行时的机器码转化时间,应用的启动和执行都会变得更加快速。

    ART优点:

    1、系统性能的显著提升。

    2、应用启动更快、运行更快、体验更流畅、触感反馈更及时。

    3、更长的电池续航能力。

    4、支持更低的硬件。

    ART缺点:

    1、更大的存储空间占用,可能会增加10%-20%。

    2、更长的应用安装时间。

    总的来说ART的功效就是“空间换时间”。

    三、初步感知ART

    1、设备如何选择ART运行环境

    通过以下步骤开启:设置→关于手机→点击最下面的版本号7次→开发者模式出现→返回→进入开发者模式,然后选择runtime"-"使用ART"

    目前官方Android模拟器并不能开启ART模式,可以通过该网址下载支持ART模式的Android模拟器镜像     ( 外链网址已屏蔽)

    2、Dalvik切换ART后系统变化

    2.1、应用安装时采用的代码优化方式不同:

    Dalvik Runtime  :  dex2opt(外链网址已屏蔽)

    ART Runtime    : dex2oat (外链网址已屏蔽)

    A130658431-77061.png_small.png

    2.2、优化后的文件大小及格式不同:

    两个运行环境产生的优化代码路径及文件名都为:/data/dalvik-cache/app/data@app@{package name}.apk@classes.dex

    ART环境产生的优化代码文件大小明显比Dalvik环境产生大:

    A130700603-77062.png_small.png

    虽然都为.dex文件结尾,但是文件格式却是天壤之别:

    ART环境文件格式:ELF Shared Object

    A130703290-77063.png_small.png

    Dalvik环境文件格式:

    A130705525-77064.png_small.png

    三、ART相关源代码

    ART相关源代码下载地址:(外链网址已屏蔽)

    从源代码文件目录名称可以很清楚的了解各文件夹中相关文件的功能,我们最关心的主要有compiler、dex2oat、runtime三个文件夹:

    compiler:主要负责Dalvik字节码到本地代码的转换,编译为libart-compiler.so

    dex2oat :完成DEX文件到ELF文件转换。编译为dex2oat

    runtime :Android ART运行时源代码,编译为libart.so

    A130707853-77065.png_small.png

    四、dex2oat优化流程

    在应用安装时,installd通过dex2oat优化APK安装包classes.dex的Dalvik字节码为本地机器代码。整个优化为:

    A130710556-77066.png_small.png

    更多LLVM编译器的信息可查看:

    五、OAT文件格式

    OAT文件其实就是基于ELF格式的一种私有文件格式。

    OAT的ELF 段信息:

    A130712978-77067.png_small.png

    OAT的ELF export信息:

    A130715228-77068.png_small.png

    OAT文件加载流程,通过分析ART相关源码,当通过DexClassLoader加载一个OAT文件基本流程如下:

    1、读取oatdata符号地址获取Oat数据 startAddress。

    2、读取oatlastword符号地址获取OAT数据 endAddress。

    3、通过startAddress和endAddress定位Oat数据。

    4、解析Oat数据。构建方法定位所需数据结构。

    然后就可以调用加载OAT文件的代码了。

    整个的方法定位过程和Dalvik运行环境没有太大区别,读者可以通过(外链网址已屏蔽)来了解如何定位到一个类的某个方法。

    您可以简单的认为ART和Dalvik区别就是:Dalvik定位到的方法是Davlik字节码,但是ART定位到的方法是本地代码。仅是方法代码内容发生了变化,但是方法的定位过程基本相同。

    我们以关键的地址点为分割截取OAT文件数据段:

    ELF头:

    A130717462-77069.png

    OAT数据内容开始:(注意0x10BB位置,DexHeader开始)

    A130719806-77070.png

    可执行本地代码部分:

    A130722462-77071.png

    OAT数据结束位置:

    A130724931-77072.png

    具体的OAT文件格式可以查看源代码。

    展开全文
  • android ART hook

    2021-01-17 16:40:24
    而且最近看到阿里开源的 Dexposed 框架,已经提供了对于android art 模式下的 hook 支持,所以对照着android art 部分的源码和之前 liang 大牛放出了hook代码研究了一下ART模式下的hook原理,做个简单的整理。...

    0x00 前言

    之前一直都是在Dalvik 虚拟机上在折腾,从Android 4.4开始开始引入ART,到5.0已经成为默认选择。而且最近看到阿里开源的 Dexposed 框架,已经提供了对于android art 模式下的 hook 支持,所以对照着android art 部分的源码和之前 liang 大牛放出了hook代码研究了一下ART模式下的hook原理,做个简单的整理。关于android ART 更详尽的部分 可以阅读csdn的博客专栏《老罗的android之旅》。

    0x01 ART

    ART是Android平台上的新一代运行时,用来代替dalvik。它主要采用了AOT(Ahead Of Time)的方法,在apk安装的时候将dalvikbytecode一次性编译成arm本地指令(但是这种AOT与c语言等还是有本质不同的,还是需要虚拟机的环境支持),这样在运行的时候就无需进行任何解释或编译便可直接执行。因为Dalvik执行的是Dex字节码,通过解释器执行。虽然Dalvik也会对频繁执行的代码进行jIT生成本地机器指令来执行,但毕竟在应用程序运行过程中将Dex字节码翻译成本地机器指令也会影响到应用程序本身的执行。因此ART节省了运行时间,提高了效率,但是在一定程度上使得应用安装的时间变长,空间占用变大。

    下图是ART 的源码目录结构:

    21d06bab3ece7f0f67634bd7f78e579f.png

    中间有几个目录比较关键,

    首先是dex2oat,负责将dex文件给转换为oat文件,具体的翻译工作需要由compiler来完成,最后编译为dex2oat;

    其次是runtime目录,内容比较多,主要就是运行时,编译为libart.so用来替换libdvm.so,dalvik是一个外壳,其中还是在调用ART runtime;

    oatdump也是一个比较重要的工具,编译为oatdump程序,主要用来对oat文件进行分析并格式化显示出文件的组成结构;

    jdwpspy是java的调试支持部分,即JDWP服务端的实现。

    ART也是由zygote所启动的,与dalvik的启动过程完全一样,保证了由dalvik到ART的无缝衔接。

    整个启动过程是从app_process(/framework/base/cmds/app_process/app_main.cpp)开始的,开始的时候,创建了一个对象AppRuntime runtime,这是个单例,整个系统运行时只有一个,随着zygote 的fork过程,每个子进程只是在不断的复制指向这个对象的指针个数。然后开始执行runtime.start方法(/frameworks/base/core/jni/AndrroidRuntime.cpp)。在start方法中会对系统的属性进行判断,选择libdvm.so 或者是libart.so进行链接。

    /*start the virtual machine*/JniInvocation jni_invocation;

    jni_invocation.Init(NULL);

    JNIEnv*env;if (startVm(&mJavaVM, &env) != 0) {return;

    }

    可以在JniInvocation.Init函数中看到初始化过程

    bool JniInvocation::Init(const char*library) {

    #ifdef HAVE_ANDROID_OSchardefault_library[PROPERTY_VALUE_MAX];

    property_get("persist.sys.dalvik.vm.lib", default_library, "libdvm.so");#else

    const char* default_library = "libdvm.so";#endif

    if (library ==NULL) {

    library=default_library;

    }

    handle_=dlopen(library, RTLD_NOW);if (handle_ ==NULL) {

    ALOGE("Failed to dlopen %s: %s", library, dlerror());return false;

    }if (!FindSymbol(reinterpret_cast(&JNI_GetDefaultJavaVMInitArgs_),"JNI_GetDefaultJavaVMInitArgs")) {return false;

    }if (!FindSymbol(reinterpret_cast(&JNI_CreateJavaVM_),"JNI_CreateJavaVM")) {return false;

    }if (!FindSymbol(reinterpret_cast(&JNI_GetCreatedJavaVMs_),"JNI_GetCreatedJavaVMs")) {return false;

    }return true;

    }

    而对于libdvm.so或者libart.so都需要提供几个公用的接口,以达到从Dalvik到ART的无缝衔接。而接下的来调用的JNI_CreateJavaVM()实际上是JniInvocation中的JNI_CreateJavaVM()函数

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

    }

    在之前的JniInvocation::init中函数指针已经选择保存了libdvm.so或者libart.so中的函数地址,在这里正式开始划分Dalvik和ART启动流程。Android系统通过将ART运行时抽象成一个Java虚拟机,以及通过系统属性persist.sys.dalvik.vm.lib和一个适配层JniInvocation,就可以无缝地将Dalvik虚拟机替换为ART运行时。

    而hook代码中对于android运行模式判断也是如此,和JniInvocation::init函数中一样,都是判断系统属性值。

    static boolisArt(){charvalue[PROPERTY_VALUE_MAX];

    property_get("persist.sys.dalvik.vm.lib", value, "");

    LOGI("[+] persist.sys.dalvik.vm.lib = %s", value);return strncmp(value, "libart.so", strlen("libart.so")) == 0;

    }

    0x02  ART 中方法的调用

    还是通过源码,在ART启动过程中:

    /*frameworks/base/core/jni/AndroidRuntime.cpp*/

    void AndroidRuntime::start(const char* className, const char*options)

    {

    ......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);

    ......

    }

    }

    ......

    }

    跟入CallStaicVoidMethod() 函数

    /*art/runtime/jni_internal.cc*/

    static void CallStaticVoidMethod(JNIEnv*env, jclass, jmethodID mid, ...) {

    va_list ap;

    va_start(ap, mid);

    CHECK_NON_NULL_ARGUMENT(CallStaticVoidMethod, mid);

    ScopedObjectAccess soa(env);

    InvokeWithVarArgs(soa, NULL, mid, ap);

    va_end(ap);}

    JNI类的成员函数CallStaticVoidMethod实际上又是通过全局函数InvokeWithVarArgs来调用参数mid指定的方法。

    /*art/runtime/jni_internal.cc*/

    static JValue InvokeWithVarArgs(const ScopedObjectAccess&soa, jobject obj,

    jmethodID mid, va_list args)

    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {

    ArtMethod* method =soa.DecodeMethod(mid);

    Object* receiver = method->IsStatic() ? NULL : soa.Decode(obj);

    MethodHelper mh(method);

    JValue result;

    ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());

    arg_array.BuildArgArray(soa, receiver, args);

    InvokeWithArgArray(soa, method,&arg_array, &result, mh.GetShorty()[0]);returnresult;

    }

    函数InvokeWithVarArgs将调用参数封装在一个数组中,然后再调用另外一个函数InvokeWithArgArray来参数mid指定的方法。

    /*art/runtime/jni_internal.cc*/

    void InvokeWithArgArray(const ScopedObjectAccess& soa, ArtMethod*method,

    ArgArray* arg_array, JValue* result, charresult_type)

    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {

    uint32_t* args = arg_array->GetArray();if (UNLIKELY(soa.Env()->check_jni)) {

    CheckMethodArguments(method, args);

    }

    method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, result_type);

    }

    可以看到参数mid实际上是一个ArtMethod对象指针,因此,将它转换为一个ArtMethod指针(dalvik也是如此),于是就可以得到被调用类方法的相关信息了。

    函数InvokeWithArgArray通过ArtMethod类的成员函数Invoke来调用参数method指定的类方法。ArtMethod类的成员函数Invoke的实现如下所示:

    /*art/runtime/mirror/art_method.cc*/

    void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue*result,charresult_type) {

    ......//Push a transition back into managed code onto the linked list in thread.

    ManagedStack fragment;

    self->PushManagedStackFragment(&fragment);

    Runtime* runtime =Runtime::Current();//Call the invoke stub, passing everything as arguments.

    if (UNLIKELY(!runtime->IsStarted())) {

    ......if (result !=NULL) {

    result->SetJ(0);

    }

    }else{const bool kLogInvocationStartAndReturn = false;if (GetEntryPointFromCompiledCode() !=NULL) {

    ......

    #ifdef ART_USE_PORTABLE_COMPILER

    (*art_portable_invoke_stub)(this, args, args_size, self, result, result_type);#else(*art_quick_invoke_stub)(this, args, args_size, self, result, result_type);#endif

    if (UNLIKELY(reinterpret_cast(self->GetException(NULL)) == -1)) {//Unusual case where we were running LLVM generated code and an//exception was thrown to force the activations to be removed from the//stack. Continue execution in the interpreter.

    self->ClearException();

    ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result);

    self->SetTopOfStack(NULL, 0);

    self->SetTopOfShadowStack(shadow_frame);

    interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result);

    }

    ......

    }else{

    ......if (result !=NULL) {

    result->SetJ(0);

    }

    }

    }//Pop transition.

    self->PopManagedStackFragment(fragment);

    }

    整个过程的重点就在art_protable_invoke_stub 和 art_quick_invoke_stub上,这也是整个hook工作的关键。函数中根据预定义宏ART_USE_PORTABLE_COMPILER来判断是protable 还是 quick 的方式。这里的protable 和 quick是android对于编译dex文件采用的两种不同的后端,protable生成的oat文件和传统的so,dll文件类似,处理不同模块之间的调用关系时需要重定位操作,而quick是通过线程的TLS中的跳转表来实现,不需要重定位操作,因此加载的速度更快。而android默认的是采用的quick,所以我们只分析quick的调用过程,也就是这里的art_quikc_invoke_stub。更详细的过程参考《老罗的android之旅》。

    我们继续看art_quick_invoke_stub的源码:

    /*art/runtime/arch/arm/quick_entrypoints_arm.S*/

    /*

    * Quick invocation stub.

    * Onentry:* r0 = method pointer

    * r1 = argument arrayorNULL for no argument methods

    * r2 = size of argument arrayinbytes

    * r3 = (managed) thread pointer

    * [sp] = JValue* result

    * [sp +4] = result type char

    */

    ENTRY art_quick_invoke_stubpush{r0, r4, r5, r9, r11, lr} @ spill regs

    .save {r0, r4, r5, r9, r11, lr}

    .pad #24.cfi_adjust_cfa_offset24.cfi_rel_offset r0,0.cfi_rel_offset r4,4.cfi_rel_offset r5,8.cfi_rel_offset r9,12.cfi_rel_offset r11,16.cfi_rel_offset lr,20

    movr11, sp @ save the stack pointer

    .cfi_def_cfa_register r11mov r9, r3 @ move managed thread pointer intor9movr4, #SUSPEND_CHECK_INTERVAL @ reset r4 to suspend check intervaladd r5, r2, #16 @ create space for method pointer inframeand r5, #0xFFFFFFF0 @ align frame size to 16bytessubsp, r5 @ reserve stack space for argument arrayadd r0, sp, #4@ pass stack pointer + method ptr as dest for memcpy

    bl memcpy @ memcpy (dest, src, bytes)

    ldr r0, [r11] @ restore method*

    ldr r1, [sp, #4] @ copy arg value for r1

    ldr r2, [sp, #8] @ copy arg value for r2

    ldr r3, [sp, #12] @ copy arg value for r3mov ip, #0 @ set ip to 0

    strip, [sp] @ store NULL for method* at bottom of frame

    ldr ip, [r0, #METHOD_CODE_OFFSET] @ get pointer to the code

    blx ip @callthe methodmovsp, r11 @ restore the stack pointer

    ldr ip, [sp, #24] @ load the result pointer

    strd r0, [ip] @ store r0/r1intoresult pointerpop{r0, r4, r5, r9, r11, lr} @ restore spill regs

    .cfi_adjust_cfa_offset -24bx lr

    END art_quick_invoke_stub

    前面的注释列出了 函数art_quick_invoke_stub被调用的时候,寄存器r0-r3的值,以及调用栈顶端的两个值。其中,

    r0指向当前被调用的类方法,

    r1指向一个参数数组地址,

    r2记录参数数组的大小,

    r3指向当前线程,

    调用栈顶端的两个元素分别用来保存调用结果及其类型。

    真正调用类方法的汇编指令如下:

    ldr ip, [r0, #METHOD_CODE_OFFSET] @ get pointer to the code

    blx ip @call the method

    这里的 METHOD_CODE_OFFSET 就是在ArtMethod*结构体中的偏移

    /*art/runtime/asm_support.h*/

    // Offset of fieldMethod::entry_point_from_compiled_code_

    #define METHOD_CODE_OFFSET40

    就是进入类方法的入口点,entry_point_from_compiled_code_字段,也是hook点。

    0x03 调用约定

    ART 其实也有两种执行模式,一种是本地机器指令,一种是类似于虚拟机的解释执行。ArtMethod结构体中的两个成员就和类方法入口有关:

    //Compiled code associated with this method for callers from managed code.

    const void* entry_point_from_compiled_code_; //本地机器指令入口 code_offset / GetCompiledCodeToInterpreterBridge (art_quick_to_interpreter_bridge)//Called by the interpreter to execute this method.

    EntryPointFromInterpreter* entry_point_from_interpreter_; //解释执行入口 artInterpreterToInterpreterBridge / artInterpreterToCompiledCodeBridg

    这两个成员都指针,其中EntryPointFromInterpreter* 是函数指针类型,实际上也就是一种调用,表示调用者是来自解释执行方式的一种调用约定

    typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper&mh,const CodeItem* code_item, ShadowFrame*shadow_frame,

    JValue* result);

    entry_point_from_interpreter_ 是作为调用者是解释执行的入口函数,也是分为两种情况:

    1.当前ArtMethod对应的方法如果是解释执行话,将entry_point_from_interpreter_ 设置为artInterpreterToInterpreterBridge;

    2.当前ArtMethod 对应的是方法是机器指令的话,就entry_point_from_interpreter_设置为artInterpreterToCompiledCodeBridge

    而entry_point_from_compiled_code_表示调用者是机器指令的类方法入口,而他的值也是分为两种情况:

    1.被调用的方法,也就是ArtMethod 所对应的方法如果需要通过解释执行,则赋值为GetCompiledCodeToInterpreterBridge() 函数的返回值;

    2.ArtMethod 所对应的方法如果是本地机器指令,则直接指向方法在oat文件中的指令。

    这两个字段的值的问题,更详细的可以阅读android art/runtime/class_linker.cc 文件中LinkCode()方法的源码,而这里我们hook的就是针对entry_point_from_compiled_code_ 字段。

    可以通过art_quick_invoke_stub 汇编代码得出在调用ArtMethod 应该方法的执行入口时的栈帧布局:

    -(low)| caller(Method *) |

    | argN |

    | callee(Method *) |

    前三个参数还会额外地保存在寄存器r1、r2和r3中。这样对于小于等于3个参数的类方法,就可以通过访问寄存器来快速地获得参数。

    注意,传递给被调用类方法的参数并不是从栈顶第一个位置(一个位置等于一个字长,即4个字节)开始保存的,而是从第二个位置开始的,即sp + 4。这是因为栈顶的第一个位置是预留用来保存用来描述当调用类方法(Caller)的ArtMethod对象地址的。由于函数art_quick_invoke_stub是用来从外部进入到ART运行时的,即不存在调用类方法,因此这时候栈顶第一个位置会被设置为NULL。

    0x04  Hook

    之前说过,Method的id也就是jmethod实际上是一个指针,指向的就是代码类方法的ArtMethod结构体,通过类型转换就可以获得目标类方法的ArtMethod的指针

    ArtMethod *artmeth = reinterpret_cast(methid);

    获得了ArtMethod* ,就可以设置类方法的entrypoint:

    if(art_quick_dispatcher != artmeth->GetEntryPointFromCompiledCode()){

    uint64_t (*entrypoint)(ArtMethod* method, Object *thiz, u4 *arg1, u4 *arg2);

    entrypoint= (uint64_t (*)(ArtMethod*, Object *, u4 *, u4 *))artmeth->GetEntryPointFromCompiledCode();

    info->entrypoint = (const void *)entrypoint;

    info->nativecode = artmeth->GetNativeMethod();

    artmeth->SetEntryPointFromCompiledCode((const void *)art_quick_dispatcher);

    也就是如果替换了entry_point_from_compiled_code_的值,使其指向我们的代码art_quick_diapatcher,这时art_quick_invoke_stub调用我们自己的代码,但是调用约定并不是普通的arm下C/C++的调用约定,所以我们需要用汇编代码来对堆栈进行处理,然后再调用真正的额外执行的C++代码,而在C++代码中也需要返回原始的方法,同样的也需要对堆栈进行处理,同样需要借助汇编来还原堆栈,调用原始的entrypoint。

    ENTRY art_quick_dispatcherpush {r4, r5, lr} @ sp - 12

    movr0, r0 @ pass r0 to methodstr r1, [sp, #(12 + 4)] @ arg arraystr r2, [sp, #(12 + 8)]str r3, [sp, #(12 + 12)]movr1, r9 @ pass r1 to threadadd r2, sp, #(12 + 4) @ pass r2 to args arrayadd r3, sp, #12@ pass r3 to old SP

    blx artQuickToDispatcher @ (Method* method, Thread*, u4 **, u4 **)pop {r4, r5, pc} @ return on success, r0 andr1 hold the result

    END art_quick_dispatcher

    上面的汇编代码art_quick_dispatcher就是替换原始entrypoint的值,处理堆栈,然后调用自己的C++函数artQuickToDispatcher(),之后在artQuickToDispatcher()调用原始的entrypoint。当然这里对于原始的entrypoint是不能直接进行调用的,需要在利用一段汇编代码,将堆栈还原成art_quick_invoke_stub调用entrypoint时的样子。

    /*

    *

    * Art QuickCallEntrypoint

    * Onentry:* r0 = method pointer

    * r1 = thread pointer

    * r2 = args arrays pointer

    * r3 = old_sp

    * [sp] = entrypoint

    */

    ENTRY art_quick_call_entrypointpush {r4, r5, lr} @ sp - 12

    sub sp, #(40 + 20) @ sp - 40 - 20

    str r0, [sp, #(40 + 0)] @ var_40_0 = method_pointerstr r1, [sp, #(40 + 4)] @ var_40_4 = thread_pointerstr r2, [sp, #(40 + 8)] @ var_40_8 = args_arraystr r3, [sp, #(40 + 12)] @ var_40_12 = old_spmovr0, spmovr1, r3

    ldr r2, =40blx memcpy @ memcpy(dest, src, size_of_byte)

    ldr r0, [sp, #(40 + 0)] @ restore method to r0

    ldr r1, [sp, #(40 + 4)]movr9, r1 @ restore thread to r9

    ldr r5, [sp, #(40 + 8)] @ pass r5 to args_array

    ldr r1, [r5] @ restore arg1

    ldr r2, [r5, #4] @ restore arg2

    ldr r3, [r5, #8] @ restore arg3

    ldr r5, [sp, #(40 + 20 + 12)] @ pass ip to entrypoint

    blx r5add sp, #(40 + 20)pop {r4, r5, pc} @ return on success, r0 andr1 hold the result

    END art_quick_call_entrypoint

    也就是art_quick_call_entrypoint恢复原来的堆栈,调用原始的entrypoint。但是,还有一个问题存在,也就是ART中关于延迟加载的问题。

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 /*art/runtime/class_linker.cc*/

    2 static void LinkCode(SirtRef<:artmethod>& method, const OatFile::OatClass*oat_class,3 uint32_t method_index)4 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {5 //Method shouldn't have already been linked.6 //判断类方法是否已经加载链接了

    7 DCHECK(method->GetEntryPointFromCompiledCode() ==NULL);8 //Every kind of method should at least get an invoke stub from the oat_method.9 //non-abstract methods also get their code pointers.

    10

    11 /*method_index描述的索引号可以在oat_class表示的OatClass结构体中找到一个OatMethod结构体oat_method。12 这个OatMethod结构描述了类方法method的本地机器指令相关信息,13 通过调用它的成员函数LinkMethod可以将这些信息设置到参数method描述的ArtMethod对象中去14 */

    15 const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);16 //在LinkMethod中将ArtMethod中的entry_point_from_compiled_code_设置为code_offset

    17 oat_method.LinkMethod(method.get());18

    19 //Install entry point from interpreter.

    20 Runtime* runtime =Runtime::Current();21 boolenter_interpreter=NeedsInterpreter(method.get(), method->GetEntryPointFromCompiledCode());22 /*为了统一管理,为一个类方法都设置一个解释器入口点。需要通过解释执行的类方法的解释器入口点函数是artInterpreterToInterpreterBridge,23 它会继续通过解释器来执行该类方法。需要通过本地机器指令执行的类方法的解释器入口点函数是artInterpreterToCompiledCodeBridge,24 它会间接地调用该类方法的本地机器指令。*/

    25 if(enter_interpreter) {26 //需要解释执行 设置entry_point_from_interpreter_

    27 method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);28 } else{29 //native code

    30 method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);31 }32

    33 if (method->IsAbstract()) {34 //设置entry_point_from_compiled_code_

    35 method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge());36 return;37 }38 //trampoline 延迟链接

    39 if (method->IsStatic() && !method->IsConstructor()) {40 //For static methods excluding the class initializer, install the trampoline.41 //It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines42 //after initializing class (see ClassLinker::InitializeClass method).

    43 method->SetEntryPointFromCompiledCode(GetResolutionTrampoline(runtime->GetClassLinker()));44 } else if(enter_interpreter) {45 //Set entry point from compiled code if there's no code or in interpreter only mode.

    46 method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge());47 }48

    49 if (method->IsNative()) {50 //Unregistering restores the dlsym lookup stub.

    51 method->UnregisterNative(Thread::Current());52 }53

    54 //Allow instrumentation its chance to hijack code.

    55 runtime->GetInstrumentation()->UpdateMethodsCode(method.get(),56 method->GetEntryPointFromCompiledCode());57 }

    LinkCode 源码

    在LinkCode() 的源码中可以看到这句代码:

    //trampoline 延迟链接

    if (method->IsStatic() && !method->IsConstructor()) {//For static methods excluding the class initializer, install the trampoline.//It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines//after initializing class (see ClassLinker::InitializeClass method).

    method->SetEntryPointFromCompiledCode(GetResolutionTrampoline(runtime->GetClassLinker()));

    }

    将entrypoint的设置为GetResolutionTrampoline() 的返回值,而这里就是

    /*art/runtime/entrypoints/entrypoint_utils.h*/

    static inline const void*GetCompiledCodeToInterpreterBridge() {#if defined(ART_USE_PORTABLE_COMPILER)

    returnGetPortableToInterpreterBridge();#else

    returnGetQuickToInterpreterBridge();#endif}

    这里就是延迟链接,意思是在加载和链接类的时候,部分方法的entrypoint设置的并不是本地机器指令,或者解释执行的入口,而是一个代理函数。而这个代理函数真正是干什么的?简单来说就是延迟链接,只有当真正调用这个类方法的时候,调用trampoline 函数才会对这个类方法进行链接,设置ArtMethod*的entry_point_from_compiled_code_的值为真正的本地机器指令或者解释执行入口。那这时在之前设置的entry_point_from_compiled_code_ 的值为art_quick_dispatcher的地址就被覆盖调用了,所以需要在我们自己的artQuickToDispatcher调用完原始的entrypoint以后,再对entrypoint进行一次判断和赋值:

    /** 处理的就是trampoline 在调用原来的tramp方法以后,重新绑定entry_pooint_from_complied_ 字段*/entrypoint= method->GetEntryPointFromCompiledCode();if(entrypoint != (const void *)art_quick_dispatcher){

    LOGW("[*] entrypoint was replaced. %s->%s", info->classDesc, info->methodName);

    method->SetEntryPointFromCompiledCode((const void *)art_quick_dispatcher);

    整个ART模式下的hook流程大致就是如此。

    展开全文
  • android art 内存模型理解art虚拟机内存管理,需要先了解虚拟机的内存组织,先看一下一个app运行时内存分布情况如下图所绘:2.png12c00000-12cc0000 rw-p 00000000 00:05 18389 /dev/ashmem/dalvik-main space ...

    一 android art 内存模型

    理解art虚拟机内存管理,需要先了解虚拟机的内存组织,先看一下一个app运行时内存分布情况如下图所绘:

    153c01411352

    2.png

    12c00000-12cc0000 rw-p 00000000 00:05 18389 /dev/ashmem/dalvik-main space (region space) (deleted)

    12cc0000-13bc0000 ---p 000c0000 00:05 18389 /dev/ashmem/dalvik-main space (region space) (deleted)

    13bc0000-32c00000 rw-p 00fc0000 00:05 18389 /dev/ashmem/dalvik-main space (region space) (deleted)

    70589000-70c84000 rw-p 00000000 103:10 240 /data/dalvik-cache/arm/system@framework@boot.art

    70c84000-7118a000 r--p 00000000 103:10 135 /data/dalvik-cache/arm/system@framework@boot.oat

    7118a000-71d47000 r-xp 00506000 103:10 135 /data/dalvik-cache/arm/system@framework@boot.oat

    71d47000-71d4e000 rw-p 00000000 00:00 0 [anon:.bss]

    71d4e000-71d4f000 r--p 010c3000 103:10 135 /data/dalvik-cache/arm/system@framework@boot.oat

    71d4f000-71d50000 rw-p 010c4000 103:10 135 /data/dalvik-cache/arm/system@framework@boot.oat

    71d50000-71e04000 rw-p 00000000 00:05 18386 /dev/ashmem/dalvik-zygote space (deleted)

    71e04000-71e05000 rw-p 00000000 00:05 21823 /dev/ashmem/dalvik-non moving space (deleted)

    71e05000-75551000 ---p 00001000 00:05 21823 /dev/ashmem/dalvik-non moving space (deleted)

    75d50000-75d50000 rw-p 0374d000 00:05 21823 /dev/ashmem/dalvik-non moving space (deleted)

    12c00000 -32c00000 512M的heap空间,与[dalvik.vm.dex2oat-Xmx]: [512m] 正好对应后面依次是boot.art的加载空间,属于image space,紧跟着的是boat.oat的加载地址,Zygote分配以及预加载的地址(non-moving),在art/runtime/gc/space下是art的内存管理代码,对于各种内存以及他们之间的关系可以通过下面的类继承图可以看出,imagespace largeobjectspace zygotespace 以及heap 使用的dlmallocaspace或者rosallocspace,不同的内存使用不同的分配策略。还有一个region_space,这个是在cocurrent_copying的回收算法中使用。

    153c01411352

    1.png

    Rosalloc是后面加的内存分配算法,全称runs-of-slots memory allocator。ROS allocator的基本分配单元是slot。slot大小从16Bytes到2048Bytes,分别是16,32,48,… n*16,512,1024,2048。应该类似于内核的伙伴管理算法。

    二 堆内存回收算法

    先看一下art支持的回收算法

    enum CollectorType {

    kCollectorTypeNone,

    kCollectorTypeMS, mark-sweep 回收算法

    kCollectorTypeCMS, 并行mark-sweep 算法

    kCollectorTypeSS, // semi-space和mark-sweep 混合算法

    kCollectorTypeGSS, 分代kCollectorTypeSS.

    kCollectorTypeMC, mark-compact算法

    // Heap trimming collector, doesn't do any actual collecting.

    kCollectorTypeHeapTrim,

    kCollectorTypeCC, concurrent-copying算法

    // The background compaction of the concurrent copying collector.

    kCollectorTypeCCBackground,

    // Instrumentation critical section fake collector.

    kCollectorTypeInstrumentation,

    // Fake collector for adding or removing application image spaces.

    kCollectorTypeAddRemoveAppImageSpace,

    // Fake collector used to implement exclusion between GC and debugger.

    kCollectorTypeDebugger,

    // A homogeneous space compaction collector used in background transition

    // when both foreground and background collector are CMS.

    kCollectorTypeHomogeneousSpaceCompact,

    // Class linker fake collector.

    kCollectorTypeClassLinker,

    // JIT Code cache fake collector.

    kCollectorTypeJitCodeCache,

    // Hprof fake collector.

    kCollectorTypeHprof,

    // Fake collector for installing/removing a system-weak holder.

    kCollectorTypeAddRemoveSystemWeakHolder,

    // Fake collector type for GetObjectsAllocated

    kCollectorTypeGetObjectsAllocated,

    // Fake collector type for ScopedGCCriticalSection

    kCollectorTypeCriticalSection,

    };

    Android支持的回收算法大体有

    Mark-sweep算法:还分为Sticky, Partial, Full,根据是否并行,又分为ConCurrent和Non-Concurrent

    MarkSweep::MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix)

    mark_compact 算法:标记-压缩(整理)算法

    concurrent_copying算法:

    semi_space算法:

    if (foreground_collector_type_ == kCollectorTypeCC) {

    region_space_ = space::RegionSpace::Create(kRegionSpaceName, region_space_mem_map);

    } else if (IsMovingGc(foreground_collector_type_) &&

    foreground_collector_type_ != kCollectorTypeGSS) {

    bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 1",

    main_mem_map_1.release());

    } else {

    CreateMainMallocSpace(main_mem_map_1.release(), initial_size, growth_limit_, capacity_);

    if (foreground_collector_type_ == kCollectorTypeGSS) {

    bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space 1",

    kGSSBumpPointerSpaceCapacity, nullptr);

    从上面的代码可以看出,如果是使用cocurrent_copying算法的话,使用RegionSpace分配空间。

    如果使用的是移动回收算法同时前端回收算法不是kCollectorTypeGSS的话使用BumpPointerSpace分配空间。具体的回收算法如下:

    static bool IsMovingGc(CollectorType collector_type) {

    return

    collector_type == kCollectorTypeSS ||

    collector_type == kCollectorTypeGSS ||

    collector_type == kCollectorTypeCC ||

    collector_type == kCollectorTypeCCBackground ||

    collector_type == kCollectorTypeMC ||

    collector_type == kCollectorTypeHomogeneousSpaceCompact;

    }

    GSS也使用BumpPointerSpace,只不过处理有所不同:

    具体的回收算法和内存分配算法之间的映射关系

    153c01411352

    2.png

    三 android ART垃圾回收算法选择

    使用kill –s QUIT $PID 可以查看当前虚拟机使用的垃圾回收算法,这个命令会在/data/anr下创建traces.txt文件有如下一行

    Start Dumping histograms for 1 iterations for concurrent copying

    上面这句话说明当前使用的kCollectorTypeCC 算法

    如何决定ART的回收算法,代码在

    heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),

    // Override the collector type to CC if the read barrier config.

    kUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_,

    kUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground)

    其中kUseReadBarrier的定义为:

    static constexpr bool kUseReadBarrier =

    kUseBakerReadBarrier || kUseBrooksReadBarrier || kUseTableLookupReadBarrier;

    如果kUseReadBarrier 为true的话,那么前端回收使用concurrent copying,后台使用kCollectorTypeCCBackground。

    {

    // If not set, background collector type defaults to homogeneous compaction.

    // If foreground is GSS, use GSS as background collector.

    // If not low memory mode, semispace otherwise.

    gc::CollectorType background_collector_type_;

    gc::CollectorType collector_type_ = (XGcOption{}).collector_type_; // NOLINT [whitespace/braces] [5]

    bool low_memory_mode_ = args.Exists(M::LowMemoryMode);

    background_collector_type_ = args.GetOrDefault(M::BackgroundGc);

    {

    XGcOption* xgc = args.Get(M::GcOption);

    if (xgc != nullptr && xgc->collector_type_ != gc::kCollectorTypeNone) {

    collector_type_ = xgc->collector_type_;

    }

    }

    if (background_collector_type_ == gc::kCollectorTypeNone) {

    if (collector_type_ != gc::kCollectorTypeGSS) {

    background_collector_type_ = low_memory_mode_ ?

    gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;

    } else {

    background_collector_type_ = collector_type_;

    }

    }

    args.Set(M::BackgroundGc, BackgroundGcOption { background_collector_type_ });

    }

    从上面的注释可以看出,如果不设置参数的话,前端使用CMS算法,后端如果是low memory设备的话 使用kCollectorTypeSS 如下:

    Start Dumping histograms for 1 iterations for marksweep + semispace

    如果不是Low memory设备的话, 指定为homogeneous compact

    Start Dumping histograms for 1 iterations for sticky concurrent mark sweep

    如果前端是GSS的话,后端也使用GSS

    如何手动设置ART的gc算法呢?

    dalvik.vm.gctype和dalvik.vm.backgroundgctype 参数来控制

    adb shell setprop dalvik.vm.gctype SS,preverify

    四 堆内存回收管理

    153c01411352

    3.png

    通过上面图,card_table Live_bitmap 和mark_bitmap都是对堆内存的映射,这三个被用来管理堆内存的回收。

    if (live_bitmap != nullptr && !space->IsRegionSpace()) {

    CHECK(mark_bitmap != nullptr);

    live_bitmap_->AddContinuousSpaceBitmap(live_bitmap);

    mark_bitmap_->AddContinuousSpaceBitmap(mark_bitmap);

    }

    } 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);

    }

    // Allocate the card table.

    // We currently don't support dynamically resizing the card table.

    // Since we don't know where in the low_4gb the app image will be located, make the card table

    // cover the whole low_4gb. TODO: Extend the card table in AddSpace.

    UNUSED(heap_capacity);

    // Start at 64 KB, we can be sure there are no spaces mapped this low since the address range is

    // reserved by the kernel.

    static constexpr size_t kMinHeapAddress = 4 * KB;

    card_table_.reset(accounting::CardTable::Create(reinterpret_cast(kMinHeapAddress),

    4 * GB - kMinHeapAddress));

    从注释看,card_table映射整个4G空间,其中card_table的结构如下:

    // Maintain a card table from the the write barrier. All writes of

    // non-null values to heap addresses should go through an entry in

    // WriteBarrier, and from there to here.

    class CardTable {

    public:

    static constexpr size_t kCardShift = 10;

    static constexpr size_t kCardSize = 1 << kCardShift;

    static constexpr uint8_t kCardClean = 0x0;

    static constexpr uint8_t kCardDirty = 0x70;

    static constexpr uint8_t kCardAged = kCardDirty - 1;

    一个cardtable使用一个字节空间标记,这一个字节的值是GC_CARD_CLEAN或者 GC_CARD_DIRTY。一个字节表示1<< kCardShift也就是1k的空间,也就是映射4G左右的空间,card_table大约占4M左右的空间。那么card_table到底是做什么的呢? 在并行回收算法中,为了快速定位object的变化,挨个扫描栈进行遍历这种耗时又费力的方法显然不能满足需求,因此就有了card_table这种快速定位那些object修改,遍历object后标记回收。针对card_table的管理引入了ModUnionTable 和RememberedSet,其中ModUnionTable主要是管理zygote的空间和Image space,rememberdset则主要管理no_moving space通过ProcessCards的处理我们可以看到。

    void Heap::ProcessCards(TimingLogger* timings,

    bool use_rem_sets,

    bool process_alloc_space_cards,

    bool clear_alloc_space_cards) {

    TimingLogger::ScopedTiming t(__FUNCTION__, timings);

    // Clear cards and keep track of cards cleared in the mod-union table.

    for (const auto& space : continuous_spaces_) {

    accounting::ModUnionTable* table = FindModUnionTableFromSpace(space);

    accounting::RememberedSet* rem_set = FindRememberedSetFromSpace(space);

    if (table != nullptr) {

    const char* name = space->IsZygoteSpace() ? "ZygoteModUnionClearCards" :

    "ImageModUnionClearCards";

    TimingLogger::ScopedTiming t2(name, timings);

    table->ProcessCards(); //针对modunio_table扫描dirty,放入到dirty_card中

    } else if (use_rem_sets && rem_set != nullptr) {

    DCHECK(collector::SemiSpace::kUseRememberedSet && collector_type_ == kCollectorTypeGSS)

    << static_cast(collector_type_);

    TimingLogger::ScopedTiming t2("AllocSpaceRemSetClearCards", timings);

    rem_set->ClearCards();

    } else if (process_alloc_space_cards) {

    TimingLogger::ScopedTiming t2("AllocSpaceClearCards", timings);

    if (clear_alloc_space_cards) {

    uint8_t* end = space->End();

    if (space->IsImageSpace()) {

    // Image space end is the end of the mirror objects, it is not necessarily page or card

    // aligned. Align up so that the check in ClearCardRange does not fail.

    end = AlignUp(end, accounting::CardTable::kCardSize);

    }

    card_table_->ClearCardRange(space->Begin(), end);

    } else {

    // No mod union table for the AllocSpace. Age the cards so that the GC knows that these

    // cards were dirty before the GC started.

    // TODO: Need to use atomic for the case where aged(cleaning thread) -> dirty(other thread)

    // -> clean(cleaning thread).

    // The races are we either end up with: Aged card, unaged card. Since we have the

    // checkpoint roots and then we scan / update mod union tables after. We will always

    // scan either card. If we end up with the non aged card, we scan it it in the pause.

    card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(),

    VoidFunctor());

    }

    }

    }

    }

    1 针对modunio_table 执行ProcessCards()

    void ModUnionTableCardCache::ProcessCards() {

    CardTable* const card_table = GetHeap()->GetCardTable();

    ModUnionAddToCardBitmapVisitor visitor(card_bitmap_.get(), card_table);

    // Clear dirty cards in the this space and update the corresponding mod-union bits.

    card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor);

    }

    主要是将修改的table添加到bitmap_中

    class ModUnionAddToCardBitmapVisitor {

    public:

    ModUnionAddToCardBitmapVisitor(ModUnionTable::CardBitmap* bitmap, CardTable* card_table)

    : bitmap_(bitmap), card_table_(card_table) {}

    inline void operator()(uint8_t* card,

    uint8_t expected_value,

    uint8_t new_value ATTRIBUTE_UNUSED) const {

    if (expected_value == CardTable::kCardDirty) {

    // We want the address the card represents, not the address of the card.

    bitmap_->Set(reinterpret_cast(card_table_->AddrFromCard(card)));

    }

    }

    2 针对remember_set,执行ClearCards(),添加到dirty_cards_中

    void RememberedSet::ClearCards() {

    CardTable* card_table = GetHeap()->GetCardTable();

    RememberedSetCardVisitor card_visitor(&dirty_cards_);

    // Clear dirty cards in the space and insert them into the dirty card set.

    card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), card_visitor);

    }

    3 现在只针对mark_sweep算法来看其他space在ProcessCards中,clear_alloc_space_cards为true,card_table_->ClearCardRange(space->Begin(), end);主要工作是清除card_table。如果是stick_mark_sweep的话,只是进行age操作

    card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(),

    VoidFunctor());

    第一章中有描述,一个java进程的空间分为三大类zygote+image空间 non-moving space空间 和main space空间,通过栈空间的扫描markroots来标记main_space空间的引用,但是对于zygote non-moving space空间的话,需要通过card_table标记他们的引用。最后通过比较live_bitmap和mark_bitmap的区别来找到那些已经不被使用的Ojbect回收。

    展开全文
  • 个人认为 Android4.4 最大的更新就是针对 RAM 占用进行了优化并且推出 ART 来代替 Dalvik,其甚至可以在一些仅有 512MB RAM 的老款手机上流畅运行。它也进一步优化了系统在低配硬件上的运行效果,似乎是为了更好地在...
  • Android ART和JVM的差异

    2021-06-03 18:21:01
    ART全称是Android Runtime,ART不符合JVM标准,所以不属于JVM,ART基于寄存器的数据结构,是专门为Android系统定制的虚拟机,通过编译工具链将基于栈的Java字节码转换为基于寄存器结构(内存中虚拟出的寄存器)的dex...
  • art 优化 dex 到本地机器指令 (还保存为以前的 odex 文件) 在ART运行时中,APK在安装的时候,同样安装服务PackageManagerService会通过守护进程installd调用另外一个工具dex2oat对打包在APK里面包含有Dex字节码进...
  • [Android ART运行时无缝替换Dalvik虚拟机的过程分析](AndroidART%E8%BF%90%E8%A1%8C%E6%97%B6%E6%97%A0%E7%BC%9D%E6%9B%BF%E6%8D%A2Dalvik%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%9A%84%E8%BF%87%E7%A8%8B%E5%88%86%E6%9E%...
  • 之前有被问到过一个面试题,问Android里的垃圾回收算法是什么?当时有点懵逼,后来查了一下原来Android官网是有说明的,原文地址链接如下: https://source.android.com/devices/tech/dalvik/gc-debug 以下是摘抄,...
  • Android Art Hook 技术方案by 低端码农 at 2015.4.130x1 开始Anddroid上的ART从5.0之后变成默认的选择,可见ART的重要性,目前关于Dalvik Hook方面研究的文章很多,但我在网上却找不到关于ART Hook相关的文章,甚至...
  • 开源的Android系统需要配置到不同硬件配置的设备上,为了降低应用的开发难度,在Linux底层之上构筑了一个名为"Dalvik"的虚拟机,名为虚拟机,实际是一种应用编译模式,Android4.4开始引入的ART模式是一种全新的应用...
  • ART模式英文全称为:Android runtime,谷歌Android 4.4系统新增的一种应用运行模式,与传统的Dalvik模式不同,ART模式可以实现更为流畅的安卓系统体验,对于大家来说,只要明白ART模式可让系统体验更加流畅,不过...
  • 而导致 Hook 不到的原因不仅仅是 inline 优化,在 Android O 之前 Inline 只是小头,真正主要的原因是 Art Optimizing 代码生成的 Sharpening 优化。Quick & OptimizingART 中的 Compile...
  • 这几天在做一个项目时需要在Android中使用OSGi框架(Apache Felix),于是在一个android 4.4.2 版本系统的某品牌的平板上实验。实验内容很简单:把felix包里的felix.jar包和一些bundles的jar包用android sdk里的dx及...
  • Android虚拟机中JIT的使用Android ART虚拟机中,通过使用JIT与AOT相互配合,在应用程序的安装和运行阶段进行优化Android DMV虚拟机和早期ART虚拟机的问题Android DVM虚拟机在应用程序每次运行时,字节码都要经常JIT...
  • } Everything worked (jni build/ android buid / android-java calls to cpp) up until android 5 build using ndk 10-C what's new with android5/ART appears to be 'check_jni.cc' and some kind of strict ...
  • 原创内容,转载请注明...插件化(二)-虚拟机启动耗时分析(三)-ART编译分析我们都知道,ART虚拟机引入了AOT预编译,最终编译任务是交由dex2oat来处理的。dex2oat是一个Android系统的二进制可执行文件,保存在system...
  • 笔者存放的位置是~/workspace/aosp/android-7.0,工程名为android-7.0。 接下来我们要先设置源码文件的后缀名。在ART中,C++的实现文件以.cc为文件后缀名。而汇编源码存储在以.S为后缀的文件里。Source Insight默认...
  • safe_mode_ && (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) && jit_.get() == nullptr) { // Note that when running ART standalone (not zygote, nor zygote fork), // the jit...
  • 一、Android系统性能提升之路Dalvik虚拟机作为Android平台的核心组成部分之一,允许在有限的1、DEX代码安装时或第一次动态加载时odex化处理。2、Android2.2版本提供了JIT机制提升性能,号称性能提升3~5倍。3、提升...
  • 有一个项目叫做Android-X86,它是一个可以在X86平台上运行的Android系统。目前许多市面上销售的Intel Atom处理器的平板电脑,就是使用的这个系统。对于普通用Java代码编写的程序来说,理论上在Android-X86平台上运行...
  • 此时,全书的主体内容已完全确定,笔者在学习ART虚拟机以及编撰本书的过程中所遇到的问题、总结的经验和教训等才可以完整地汇总并分享给各位读者。所以,本章是全书的点睛之笔,为必读章节。并且,我相信随着读者...
  • 0x00 概述ARTAndroid平台上的新一代运行时,用来代替dalvik。它主要采用了AOT的方法,在apk安装的时候将dalvikbytecode一次性编译成arm本地指令(但是这种AOT与c语言等还是有本质不同的,还是需要虚拟机的环境支持)...
  • startVm————》JNI_CreateJavaVM---》Runtime::Create---》Runtime::Initclasslinker的建立bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {...intern_table_ = new InternTable;...
  • 相信作为原生Android 4.4的一项特色功能,不少同学都会想要体验一下原汁原味ART模式吧。据说在ART模式下Android将不再卡顿,丝丝顺滑的流畅度直逼iOS。当然也有不少同学在把手机切换到ART模式重启之后卡在开机画面...
  • YAHFA IntroductionYAHFA is a hook framework for Android ART. It provides an efficient way for Java method hooking or replacement. Currently it supports:Android 5.0(API 21)Android 5.1(API 22)Android .....
  • // if (art_work_around_app_jni_bugs) { // targetAddress = (size_t)art_work_around_app_jni_bugs; // } int offset = 0; bool found = false; size_t addr = 0; while (true) { addr = startAddress + ...
  • ./framework/boot.vdex ./framework/arm/boot.vdex ./framework/arm/boot.oat ./framework/arm/boot.art ./framework/arm64/boot.vdex ./framework/arm64/boot.oat ./framework/arm64/boot.art Android8.0以后修改...
  • 1.2 准备环境和工具为了更好学习ART,读者要准备好如下的环境或工具。1.2.1 准备源代码首先,我们需要一份Android 7.0的源代码。笔者在百度云盘上提供了本书所需的资料下载。读者也可以到清华大学开源软件镜像站...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,235
精华内容 10,894
关键字:

安卓art