返回

ART和JVM的区别

2022-11-01 by zhangyong

ART基于寄存器,JVM基于栈

堆区: 只存放类对象,实例化的数据放在堆区;堆区的数据放在主内存
方法区:又叫静态存储区,存放class文件和静态数据,全局变量;方法区的数据在主内存
栈区:存放方法局部变量,方法,线程不共享; 栈区的数据在高速缓冲区

主内存 高速缓冲区

堆区和方法区在内存条上
栈区在cpu的高速缓冲区上
因为cpu的读取速度在2.4G左右,内存条的速度1333M、1666M,两者之间有差异所以需要一个高速缓冲区(三级缓存)

ART基于寄存器

public  Singleton getInstance() {
    Singleton instance = new Singleton();
    return instance;
}

翻译成arm指令

0000: new-instance v0, Singleton // type@0000
0002: invoke-direct {v0}, Singleton.<init>:()V // method@0000
0005: return-object v0

JVM基于栈

IDEA字节码

栈结构

ART执行效率高

更快的本机方法

使用 @FastNative@CriticalNative 注解可以更快地调用Java Native Interface (JNI)。这些内置的 ART 运行时优化可加快 JNI 转换并替换现在已弃用的!bang JNI表示法。注释对非本机方法没有影响,并且仅可用于引导类bootclasspath上的平台 Java 语言代码(无 Play 商店更新)。

@FastNative注解支持非静态方法。如果方法将jobject作为参数或返回值访问,请使用此选项。

@CriticalNative注解提供了一种更快的方式来运行本机方法,但有以下限制:

方法必须是静态的——没有参数、返回值或隐式this的对象。
只有原始类型被传递给本机方法。
本机方法在其函数定义中不使用JNIEnv和jclass参数。
该方法必须使用RegisterNatives ,而不是依赖动态 JNI 链接。
@FastNative和@CriticalNative注释在执行本机方法时禁用垃圾收集。不要与长时间运行的方法一起使用,包括通常快速但通常不受限制的方法。

垃圾收集暂停可能会导致死锁。如果锁定尚未在本地释放(即在返回托管代码之前),则不要在快速本机调用期间获取锁定。这不适用于常规 JNI 调用,因为 ART 将执行的本机代码视为已挂起。

@FastNative可以将本机方法性能提高多达 3 倍, @CriticalNative CriticalNative 可以提高多达 5 倍。

class ArtMethod   {
    // The hotness we measure for this method. Managed by the interpreter. Not atomic, as we allow
    // 通过注释可以知道  该变量时标记经过 java方法运行的频率,
    uint16_t hotness_count_;

    struct PtrSizedFields {
    // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
    //通过google给我们的注释 可以知道,缓存一个方法对应的arm指令,快速的得到结果
    ArtMethod** dex_cache_resolved_methods_;
    //上面方法执行的结果 数据缓存在data_ 变量中,下次执行经过@FastNative修饰的java方法时,直接取出数据
    void* data_;
    //arm指令在内存中的地址,快速的定位到arm指令
    void* entry_point_from_quick_compiled_code_;
} ptr_sized_fields_;  

dex优化

dex文件结构

odex

因为 apk 实际为 zip 压缩包,虚拟机每次加载都需要从 apk 中读取classes.dex 文件,这样会耗费很多的时间,而如果采用了 odex 方式优化的 dex 文件,他包含了加载 dex 必须的依赖库文件列表,只需要直接加载而不需要再去解析。

在 Android N 之前,对于在 dalvik 环境中 使用 dexopt 来对 dex 字节码进行优化生成 odex 文件最终存在手机的 data/dalvik-cache 目录下,最后把 apk 文件中的 dex 文件删除。

在 Android O 之后,odex 是从 vdex 这个文件中 提取了部分模块生成的一个新的可执行二进制码文件 , odex 从 vdex 中提取后,vdex 的大小就减少了。

第一次开机就会生成在 /system/app/<packagename>/oat/ 下
在系统运行过程中,虚拟机将其 从 /system/app 下 copy 到 /data/davilk-cache/ 下;
odex + vdex = apk 的全部源码 (vdex 并不是独立于 odex 的文件 odex + vdex 才代表一个 apk

AOT

ART 推出了预先 (AOT) 编译,可提高应用的性能。ART 还具有比 Dalvik 更严格的安装时验证。在安装时,ART 使用设备自带的 dex2oat 工具来编译应用。该实用工具接受 DEX 文件作为输入,并针对目标设备生成已编译应用的可执行文件。之后打开 App 的时候,不需要额外的翻译工作,直接使用本地机器码运行,因此运行速度提高。

AOT 是 art 的核心,oat 文件包含 oatdata 和 oatexec。前者包含 dex 文件内容,后者包含生成的本地机器指令,从这里看出 oat 文件回会比 dex 文件占用更大的存储空间。

因为 oat 文件包含生成的本地机器指令进而可以直接运行,它同样保存在手机的 data/dalvik-cache 目录下 PMS(PackgetManagerService)—>installd(守护进程)——>dex2oat(/system/bin/dex2oat) 。注意存放在 data/dalvik-cache 目录下的后缀名都仍为 .dex 前者其实表示一个优化过的 .dex 文件 后者为 .art 文件。

push 一个新的 apk 文件覆盖之前 /system/app 下 apk 文件,会触发 PKMS 扫描时下发 force_dex flag ,强行生成新的 vdex文件 ,覆盖之前的vdex 文件,由于某种机制,这个新 vdex 文件会 copy 到 /data/dalvik-cache/ 下,于是 art 文件也变化了。

引用文档

Android系统8.0为什么比7.0要快50%,深入虚拟机源码发现答案 https://www.jianshu.com/p/41373fc26fd1
art改进 https://source.android.com/docs/core/runtime/improvements?hl=zh-cn
从JVM到Dalivk再到ART https://blog.csdn.net/stven_king/article/details/102620708