这是我参与「第四届青训营 」笔记创作活动的第9天
安卓整体架构
下图中黄色的部分,就是ART,给APP和system server进程提供运行环境,包括JAVA语法支持,托管,性能优化,debug等等一系列的能力。
ART架构
(ART)Android Runtime
对于运行 Android 5.0(API 级别 21)或更高版本的设备,每个应用都在其自己的进程中运行,并且有其自己的 Android Runtime (ART) 实例。ART 编写为通过执行 DEX 文件在低内存设备上运行多个虚拟机,DEX 文件是一种专为 Android 设计的字节码格式,经过优化,使用的内存很少。编译工具链(例如 Jack)将 Java 源代码编译为 DEX 字节码,使其可在 Android 平台上运行。
ART 的部分主要功能包括:
- 预先 (AOT) 和即时 (JIT) 编译
- 优化的垃圾回收 (GC)
- 更好的调试支持,包括专用采样分析器、详细的诊断异常和崩溃报告,并且能够设置监视点以监控特定字段
在 Android 版本 5.0(API 级别 21)之前,Dalvik 是 Android Runtime。如果您的应用在 ART 上运行效果很好,那么它应该也可在 Dalvik 上运行,但反过来不一定。
Android 还包含一套核心运行时库,可提供 Java API 框架使用的 Java 编程语言大部分功能,包括一些 Java 8 语言功能。
对象篇
类的管理
类主要描述一个对象的内存布局和函数信息,类管理决定一个对象的内存布局和函数信息
内存布局:类成员的大小,类型,和排布
函数信息:主要是虚表的信息:某个函数定义在当前类函数表的第几个位置因为java是支持继承的,因此类的内存布局和函数虚表需要做继承链全展开以后才能真正确认
Object类的定义和结构
类加载
第一次分配对象大小,是由继承链决定的
Java 的类,是在第一次使用的时候,才会进行加载
一个类的直接父类和间接父类和自己的所有成员的大小就是这个类的大小
内存布局
内存分配
APP 的 Java 对象内存分配上是托管到 VM 来处理的,并不会直接向操作系统去申请,实际上对 OS 内存的占用和内存布局,是 VM 控制的
内存分配器
TLAB 是每个线程一个局部的 cache ,体积小,分配快,适用于小量内存临时变量
ROSallocator 直接从虚拟机的堆区去取(VM mem pool),适用于中等内存数组/容器
LOSallocator 从操作系统层(Linux)取,适用于大量内存bitmap存储图片
内存碎片
ART 内存分配的根本原理,是给使用者在最优的范围内找到一块大小符合的连续内存
内存回收
GC:垃圾回收(Garbage Collection),定期查找徐通内不用的对象,并且释放占用的内存
RC:引用技术(Reference Counting),指的是堆一个对象引用进行计数,多一个引用者则计数器+1,少一个就-1,为0就释放,是一种即时回收机制
ART 的引用
强引用:直接持有,不会被回收
软引用:内存不够时会被回收
弱引用:只要触发 GC 就会被回收
触发 GC 的条件
- 没内存了
-
该 GC
- VM堆占用达到水位
- 系统内存紧张
- 任性了,想触发了
苹果手机内存需求少的原因
同样是运行 APP,IOS 每个APP内存空间里只有在用内存(因为RC会对内存进行及时回收)。
而 Android 则因为 GC 是定时触发,不会时刻都在清理,所以每个应用都会维护一个垃圾堆,应用多了,这个垃圾堆就会变得很大。
tracing GC
从 roots 开始遍历,所有 mark 对象都是有 holder 的,释放掉没用 holder 的 object
copying GC
从 roots 遍历,把有用的对象拷贝到另一个区域,然后集中地释放掉当前区域地内存
执行
虚拟机的执行方式
三种:解释执行,JIT ,AOT
解释执行由解释器完成,JIT 和 AOT 则在编译之后执行,此时直接执行的已经是指令了
JIT (Just in time)
JIT 的 OSR 即栈上替换,某些分支还能采用更激进的优化,处理不来还能回退
AOT
AOT(或者叫 OAT)是在程序运行之前,堆APK中的函数进行编译
- 和程序是否允许无关
- 编译的范围不是以函数为单位,而是以 dex 为单位的
- 结果会持久化
为什么 AOT 一定要在端侧编译?为什么不在 APK 发布的时候,就直接发布 AOT 以后的应用?
- 不需要额外维持一块内存来保存 JIT 的结果
- 不需要预热
- 但是不能动态调整,没有 OSR 来支持栈上的替换
延迟绑定
绑定得越迟,动态性越好,性能越差
绑定得越早,动态性越差,性能越好
安卓生态比较糟糕,不同手机用得安卓版本不同(比如一个是安卓 11 一个是安卓10),无法事先确定,只能在手机端执行。往往只有在端侧(即在手机运行端上)才能获取到自己类的完整信息。
栈管理
ART 对于解释执行和编译后指令采用两种不同的策略
- 对于解释执行,栈托管到虚拟机完成
- 对于编译后的,压栈处理和native代码是一样的,遵从对应指令集的约定