一 ART
1. Dalvik
- Android4.4-
- Apk在打包的过程中: java源代码通过javac编译成.class文件,再转换成Dalvik虚拟机执行的.dex文件,也就是JIT(Just in time)即时编译。
- 启动的时候会先将.dex文件转换成快速运行的机器码.oat文件
- 缺点:app启动慢。
2. ART特点
- Android4.4+,真正5.0+取代了Dalvik
- 兼容Dalvik虚拟机的特性
- 新特性:预编译AOT(ahead of time)—— 安装APK时就将dex直接处理成虚拟机可执行的机器码.oat文件。
- 优点:
- 加快APP冷启动速度
- 提升GC速率,主要是GMS的改善
- 提供功能全面的debug特性
- 缺点:
- App安装速度慢,安装时需要生成.oat文件
- App占用内存空间大,.oat文件比字节码文件大
二 ART GC机制
- 默认CMS并发标记清除。
- 应用将进程转换到后台或者缓存等进程状态时,ART将执行对压缩。
- 引入了一种基于位图的新的内存分配程序,称为RosAlloc(插槽运行分配器),具有分片锁,当分配规模较小时刻添加线程的本地缓冲区,因而性能优于DlMalloc。
1. 与Dalvik相比的 GC优化:
- 与 Dalvik相比,暂停次数从2次减少到1次。Dalvik 的第一次暂停主要是为了进行
根标记
,即在ART中进行并发标记
,让线程标记自己的根,然后马上恢复运行。 - 与 Dalvik类似,ART GC在清除过程开始之前也会暂停1次。两者在这方面的主要差异在于:
- 在此暂停期间,某些 Dalvik环节在ART中并发进行。这些环节包括 java.lang.ref.Reference 处理、系统弱清除(例如,jni 弱全局等)、重新标记非线程根和卡片预清理。
- 在ART暂停期间仍进行的阶段包括扫描脏卡片以及重新标记线程根,这些操作有助于缩短暂停时间。
- 相对于 Dalvik,ART GC改进的最后一个方面是粘性CMS回收器增加了GC吞吐量。
- 不同于普通的分代GC,粘性 CMS 不移动。年轻对象被保存在一个分配堆栈(基本上是 java.lang.Object 数组)中,而非为其设置一个专用区域。
- 这样可以避免移动所需的对象以维持低暂停次数,但缺点是容易在堆栈中加入大量复杂对象图像而使堆栈变长。
- ART GC 引入了移动垃圾回收器,目的在于通过
堆压缩
来减少后台应用使用的内存。- 目前触发对压缩事件是ActivityManager进程状态改变。
- 当应用转到后台运行时,ART会进行一些压缩操作(压缩或监视器压缩),从而导致应用线程长时间暂停。
- 使用的是两个移动GC是同构空间压缩和半空间压缩。
2. 性能
- 评测GC性能:GC时间转储、systrace。
- 评测因素:哪些GC会导致长时间暂停或抢占应用线程。是否有过度分配,或者错误的变异器行为。。。
- 使用请求信号(SIGOUIT)获取GC性能信息
3. 分析GC正确性问题的工具
- ART内部崩溃—— 由应用代码错误造成
堆损坏
。 - 工具包括:堆验证选项、valgrind、CheckJNI。
3.1 CheckJNI
- 启用
adb shell setprop dalvik.vm.checkjni true
Forcecopy模式
对检测超出数组区域的写入非常有用。启用adb shell setprop dalvik.vm.jniopts forcecopy
3.2 valgrind
- 检测无效堆地址的读取和写入操作。
- 由于AOT编译器使用隐式null检查,因此建议使用eng版本运行valgrind。
- 执行速度慢。
3.3 验证堆
- 调试GC相关错误或堆损坏。
- 如果启动GC验证
- [no]preverify 将在GC启动前执行堆验证。
- [no]preweepingverify在启动GC器清除过程之前进行堆验证。
- [no]postverify将在GC完成清除后执行堆验证。
- [no]preverify_rosalloc、[no]postsweepingverify_rosalloc 和 [no]postverify_rosalloc 也是附加 GC 选项,仅验证 RosAlloc 内部计算的状态。验证的主要内容是,魔数值是否与预期常量匹配,以及可用内存块是否已在 free_page_runs_ 映射中注册。
ART JIT
1. JNI结构
- JIT编译器在Android应用运行时持续提高其性能。
2. JIT编译
- 用户运行应用,而这随后就会触发 ART 加载 .dex 文件。
- 如果有 .oat 文件(即 .dex 文件的 AOT 二进制文件),则 ART 会直接使用该文件。虽然 .oat 文件会定期生成,但文件中不一定会包含经过编译的代码(即 AOT 二进制文件)。
- 如果没有 .oat 文件,则 ART 会通过 JIT 或解释器执行 .dex 文件。 如果有 .oat 文件,ART 将一律使用这类文件。否则,它将在内存中使用并解压 APK 文件,从而得到 .dex 文件,但是这会导致消耗大量内存(相当于 dex 文件的大小)。
- 针对任何未根据 speed 编译过滤器编译的应用启用 JIT(也就是说,要尽可能多地编译应用中的代码)。
- 将 JIT 配置文件数据转存到只限应用访问的系统目录内的文件中。
- AOT 编译 (dex2oat) 守护进程通过解析该文件来推进其编译。
3. JIT流程
- 分析信息会存储在代码缓存中,并会在内存紧张时作为垃圾被回收。
- 无法保证在应用处于后台运行状态时所捕获的快照能够包含完整的数据(即 JIT 编译的所有内容)。
- 该过程不会尝试确保记录所有内容(因为这将影响运行时性能)。
- 方法可能有三种不同的状态:
- 已经过解释(dex 代码)
- 已经过 JIT 编译
- 已经过 AOT 编译
如果同时存在 JIT 和 AOT 代码(例如,由于反复进行逆优化),经过 JIT 编译的代码将是首选代码。
- 在不影响前台应用性能的情况下运行 JIT 所需的内存取决于相关应用。大型应用比小型应用需要更多内存。一般来说,大型应用所需的内存稳定维持在 4 MB 左右。
加载
- 加载 * 使用类加载器完成,生成字节码文件,使用类加载器的时候,会有一个双亲委派模型
- 验证
* 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
- 验证对于类加载机制而言,并不是必要的阶段,如果所运行的代码是确保安全的
- 准备 * 为类的静态变量分配内存和将其初始化为默认值,这些内存都将在方法区中进行分配。
- 解析
* 虚拟机将常量池中的符号引用替换为直接引用的过程
- 符号引用:一组符号来描述引用的对象
- 直接引用:能够直接指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄
- 初始化
* 初始化是类加载的最后一步,开始真正的执行类中定义的java程序代码。
- 初始化阶段是执行类构造器()方法的过程。这个方法是由编译器自动收集类中的所有变量的赋值动作和静态语句块中(static{}中的语句合并产生的)
- 使用
- 卸载