Android的ART和Dalvik分析

xingyun86 2018-3-28 2913

ART虚拟机的实现在libart.so中,而Dalvik再libdvm.so中
实现了Java虚拟机的接口,内部却提供完全不一样的东西, – 为了兼容。

I. Dalvik

  • 简介: 是Java虚拟机,Android L 之前Android的核心组成部分之一(Android L 后被ART取代)。
  • 作者: Dan Bornstein
  • 名字来源: 作者祖先曾经居住过的小渔村(位于冰岛)- Dalvík
  • 基本功能: 支持.dex(Dalvik Executable)运行
  • .dex: 专为Dalvik设计的一种压缩格式(Java应用程序),适合内存和处理器速度有限的系统
  • 业界评价: 一直被用户指责为拖慢安卓系统运行速度不如IOS的根源。2014年的Google I/O大会上,在Android L 中被Google 删除,取而代之的是ART。

诞生缘由

  • 业界猜测: 是对Sun尝试控制和保护来自Java ME收入来源的一次反应,以及对建立OpenJDK统辖理事会迟迟未果的回答
  • 官方解释: Dalvik是对解决目前Java ME平台上分裂(与已有的JVM)的一次尝试,也是为了提供一个拥有较少限制许可证的平台

基本组件

  • dex字节码解释器: 逐条逐行的执行字节码指令
  • JIT(Just-In-Time)编译器: 当多次运行速度稍慢的代码时,JIT编译器则会自动将热点代码编译并缓存起来,由此执行速度会逐渐加快

II. ART

Ahead-Of-Time(AOT)编译

在安装时就已经编译成本地代码,因此只需要编译一次,运行时直接执行(应用运行更快(少去该部分的资源竞争)的同时,更省电)。

  • 占用内存空间变大。
  • 运行速度更快,且更省电 - 运行程序时无需额外的编译、加载转换等,少去这块的CPU资源竞争。

III. ART与Dalvik相同之处

接口名备注
JNI_GetDefaultJavaVMInitArgs获取虚拟机的默认初始化参数
JNI_CreateJavaVM在进程中创建虚拟机实例
JNI_GetCreatedJavaVMs获取进程中创建的虚拟机实例

IV. ART与Dalvik不同之处

虚拟机名安装时每次重新运行apk时
ART解析翻译dex字节码为本地代码(AOT)直接执行本地代码
Dalvik将dex字节码优化生成odex文件(PackageManagerService请求守护进程installd来执行)通过解释器(Android 2.2引入JIT(缓存热点代码的解释结果))对dex字节码解释为机器码,再执行机器码

V. 推敲

persist.sys.dalvik.vm.lib: 系统属性,若等于libdvm.so则当前使用的是Dalvik虚拟机,若等于libstart.so,则当前使用的是ART虚拟机

1. 安装

最终安装优化/翻译 结果都是保存在odex文件中

系统启动时,系统除了对/system/app和/data/app目录下所有apk进行翻译以外,还会对/system/framework目录的apk/jar以及这些apk所引用的外部jar进行翻译,保证不对Dalvik虚拟机产生任何依赖。

2. 运行


运行相关代码段:

// frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const char* options)
{
    ......
    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {
        return;
    }
    ......
    /*
     * 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
        }
    }
    ......
}
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
    ......
    /*
     * 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;
    }
    ......
}
// libnativehelper/JniInvocation.cpp
#ifdef HAVE_ANDROID_OS
static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib";
#endif
static const char* kLibraryFallback = "libdvm.so";
bool JniInvocation::Init(const char* library) {
#ifdef HAVE_ANDROID_OS
  char default_library[PROPERTY_VALUE_MAX];
  property_get(kLibrarySystemProperty, default_library, kLibraryFallback);
#else
  const char* default_library = kLibraryFallback;
#endif
  if (library == NULL) {
    library = default_library;
  }
  handle_ = dlopen(library, RTLD_NOW);
  if (handle_ == NULL) {
    if (strcmp(library, kLibraryFallback) == 0) {
      // Nothing else to try.
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
    // Note that this is enough to get something like the zygote
    // running, we can't property_set here to fix this for the future
    // because we are root and not the system user. See
    // RuntimeInit.commonInit for where we fix up the property to
    // avoid future fallbacks. http://b/11463182
    ALOGW("Falling back from %s to %s after dlopen error: %s",
          library, kLibraryFallback, dlerror());
    library = kLibraryFallback;
    handle_ = dlopen(library, RTLD_NOW);
    if (handle_ == NULL) {
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
  }
  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;
}
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
}
jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  return JNI_CreateJavaVM_(p_vm, p_env, vm_args);
}

安装相关代码段:

// frameworks/base/services/java/com/android/server/pm/Installer.java
public final class Installer {
    ......
    public int dexopt(String apkPath, int uid, boolean isPublic) {
        StringBuilder builder = new StringBuilder("dexopt");
        builder.append(' ');
        builder.append(apkPath);
        builder.append(' ');
        builder.append(uid);
        builder.append(isPublic ? " 1" : " 0");
        return execute(builder.toString());
    }
    ......
}
// frameworks/native/cmds/installd/commands.c
int dexopt(const char *apk_path, uid_t uid, int is_public)
{
    struct utimbuf ut;
    struct stat apk_stat, dex_stat;
    char out_path[PKG_PATH_MAX];
    char dexopt_flags[PROPERTY_VALUE_MAX];
    char persist_sys_dalvik_vm_lib[PROPERTY_VALUE_MAX];
    char *end;
    int res, zip_fd=-1, out_fd=-1;
    ......
    /* The command to run depend ones the value of persist.sys.dalvik.vm.lib */
    property_get("persist.sys.dalvik.vm.lib", persist_sys_dalvik_vm_lib, "libdvm.so");
    /* Before anything else: is there a .odex file?  If so, we have
     * precompiled the apk and there is nothing to do here.
     */
    sprintf(out_path, "%s%s", apk_path, ".odex");
    if (stat(out_path, &dex_stat) == 0) {
        return 0;
    }
    if (create_cache_path(out_path, apk_path)) {
        return -1;
    }
    ......
    out_fd = open(out_path, O_RDWR | O_CREAT | O_EXCL, 0644);
    ......
    pid_t pid;
    pid = fork();
    if (pid == 0) {
        ......
        if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) {
            run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
        } else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) {
            run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
        } else {
            exit(69);   /* Unexpected persist.sys.dalvik.vm.lib value */
        }
        exit(68);   /* only get here on exec failure */
    }
    ......
}
static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name,
    const char* output_file_name, const char* dexopt_flags)
{
    static const char* DEX_OPT_BIN = "/system/bin/dexopt";
    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
    char zip_num[MAX_INT_LEN];
    char odex_num[MAX_INT_LEN];
    sprintf(zip_num, "%d", zip_fd);
    sprintf(odex_num, "%d", odex_fd);
    ALOGV("Running %s in=%s out=%s\n", DEX_OPT_BIN, input_file_name, output_file_name);
    execl(DEX_OPT_BIN, DEX_OPT_BIN, "--zip", zip_num, odex_num, input_file_name,
        dexopt_flags, (char*) NULL);
    ALOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno));
}
static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
    const char* output_file_name, const char* dexopt_flags)
{
    static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
    char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];
    char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];
    char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];
    char oat_location_arg[strlen("--oat-name=") + PKG_PATH_MAX];
    sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
    sprintf(zip_location_arg, "--zip-location=%s", input_file_name);
    sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);
    sprintf(oat_location_arg, "--oat-location=%s", output_file_name);
    ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
    execl(DEX2OAT_BIN, DEX2OAT_BIN,
          zip_fd_arg, zip_location_arg,
          oat_fd_arg, oat_location_arg,
          (char*) NULL);
    ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
}


×
打赏作者
最新回复 (0)
只看楼主
全部楼主
返回