在Android早期版本(2.2 - 4.4),移动设备普遍存在内存容量小(512MB以下)、CPU性能弱的特点。Dalvik针对这种资源受限环境设计,采用JIT即时编译技术以减少安装包体积和内存占用。随着硬件性能提升(多核CPU、大容量内存普及),用户对应用流畅度和响应速度要求提高,促使Android 5.0引入ART运行时,通过AOT预编译技术彻底改变执行模式。
Android应用数量和复杂度快速增长,Dalvik的JIT编译在长时间运行后会出现性能下降,尤其在游戏、视频编辑等高性能需求场景下表现不佳。ART的设计目标是通过预编译优化代码执行效率,同时降低运行时CPU负载,为开发者提供更接近原生应用的性能体验。
Java虚拟机技术在桌面端已成熟,但移动端需要更适配的执行环境。Dalvik作为过渡方案解决了初期兼容性问题,而ART的出现标志着Android运行时向专业化、定制化方向发展,体现了对移动场景的深度优化。
Dalvik的JIT编译器位于dalvik/vm/Jit.cpp
,核心逻辑通过JitCompileMethod
函数实现:
// dalvik/vm/Jit.cpp
bool JitCompileMethod(const Method* method) {
// 检查方法是否符合编译条件(如调用次数阈值)
if (!shouldCompile(method)) {
return false;
}
// 生成中间表示(IR)
IRGenerator irGen(method);
irGen.generateIR();
// 优化IR(简单常量折叠等)
optimizeIR(irGen.getIR());
// 生成机器码
MachineCode* machineCode = generateMachineCode(irGen.getIR());
// 替换原字节码执行路径
replaceBytecodeWithMachineCode(method, machineCode);
return true;
}
JIT采用"解释-编译"混合模式:首次执行时解释执行,当方法调用次数超过阈值后触发编译。这种策略在启动速度和内存占用间取得平衡,但存在编译延迟和优化不足问题。
ART的AOT编译由dex2oat
工具完成,核心代码位于art/tools/dex2oat/dex2oat.cc
:
// art/tools/dex2oat/dex2oat.cc
int main(int argc, char** argv) {
// 解析命令行参数,获取输入DEX文件和目标架构
CommandLineOptions options;
if (!options.Parse(argc, argv)) {
return -1;
}
// 加载DEX文件
DexFile dexFile(options.input_dex_file);
// 生成中间表示(HGraph)
HGraphBuilder hGraphBuilder(&dexFile);
HGraph* hGraph = hGraphBuilder.Build();
// 多阶段优化(全局常量传播、死代码消除等)
OptimizeHGraph(hGraph);
// 针对目标架构生成机器码
MachineCodeGenerator codeGen(hGraph, options.target_arch);
codeGen.GenerateCode();
// 输出包含机器码的OAT文件
WriteOatFile(options.output_oat_file, codeGen.GetMachineCode());
return 0;
}
AOT在应用安装阶段将DEX字节码编译为机器码,存储在.oat
文件中。执行时直接加载机器码,避免了运行时编译开销,显著提升启动速度和执行效率。
从Android 7.0开始,ART引入JIT+AOT混合模式:
Dalvik采用线性堆内存布局,核心实现位于dalvik/vm/alloc/HeapSource.cpp
:
// dalvik/vm/alloc/HeapSource.cpp
HeapSource* HeapSource::CreateHeapSource() {
// 初始化堆内存大小(默认16MB)
size_t initialSize = kDefaultHeapSize;
// 通过mmap系统调用分配连续内存
void* heapMemory = mmap(nullptr, initialSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (heapMemory == MAP_FAILED) {
// 内存分配失败处理
return nullptr;
}
// 创建堆内存管理对象
return new HeapSource(heapMemory, initialSize);
}
采用标记-清除垃圾回收算法,在dalvik/vm/gc/MarkSweep.cpp
实现:
// dalvik/vm/gc/MarkSweep.cpp
void MarkSweepGC::CollectGarbage() {
// 从根集合(栈、静态变量等)开始标记存活对象
MarkRoots();
// 扫描堆内存,清除未标记对象
SweepHeap();
// 压缩内存(可选)
CompactHeapIfNeeded();
}
这种设计在小内存设备上表现良好,但存在内存碎片问题。
ART采用分代式堆内存设计,分为:
art/runtime/gc/heap.cc
:// art/runtime/gc/heap.cc
void Heap::Initialize() {
// 初始化年轻代(默认占堆的1/8)
youngGen_.Initialize(this, kYoungGenRatio * totalHeapSize_);
// 初始化老年代
oldGen_.Initialize(this, totalHeapSize_ - youngGen_.GetCapacity());
// 初始化大对象空间
largeObjectSpace_.Initialize(this);
}
垃圾回收采用分代策略:
art/runtime/gc/collector/scavenge.cc
)art/runtime/gc/collector/marksweep.cc
)Dalvik的类加载器在dalvik/vm/ClassPath.cpp
实现:
// dalvik/vm/ClassPath.cpp
ClassObject* ClassPath::FindClass(const char* descriptor) {
// 遍历类路径(DEX文件列表)
for (const DexFile* dexFile : dexFiles_) {
// 在DEX文件中查找类
ClassObject* clazz = dexFile->FindClass(descriptor);
if (clazz != nullptr) {
return clazz;
}
}
return nullptr;
}
采用传统的双亲委托模型,但对DEX文件的处理较为简单,缺乏深度验证机制。
ART的类加载器体系在art/runtime/class_linker.cc
实现:
// art/runtime/class_linker.cc
std::unique_ptr ClassLinker::LoadClass(ClassLoader* classLoader, const DexFile& dexFile, uint32_t classDefIndex) {
// 验证类定义合法性
if (!dexFile.IsValidClassDef(classDefIndex)) {
return nullptr;
}
// 解析类引用
ResolveClassReferences(dexFile, classDefIndex);
// 链接类(准备阶段)
LinkClass(classLoader, dexFile, classDefIndex);
// 初始化类(如果需要)
InitializeClassIfNeeded(classLoader, dexFile, classDefIndex);
return std::unique_ptr(CreateClassObject(classLoader, dexFile, classDefIndex));
}
增加了以下特性:
Dalvik的JNI实现位于dalvik/vm/Jni.c
:
// dalvik/vm/Jni.c
JNIEXPORT jobject JNICALL dvmNewObject(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
// 检查类是否已初始化
if (!dvmIsClassInitialized(clazz)) {
dvmInitClass(clazz);
}
// 分配对象内存
Object* obj = dvmAllocObject(clazz, ALLOC_DONT_GC);
if (obj == nullptr) {
// 内存分配失败处理
return nullptr;
}
// 调用构造函数
va_list args;
va_start(args, methodID);
dvmCallMethodV((Method*)methodID, obj, args);
va_end(args);
return (jobject)obj;
}
JNI调用直接操作对象内存,缺乏严格的类型检查和安全防护。
ART的JNI实现位于art/runtime/jni/java_vm_ext.cc
:
// art/runtime/jni/java_vm_ext.cc
jobject JavaVMExt::NewObject(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
// 验证类和方法的有效性
if (!IsClassInitialized(clazz) || !IsValidMethodID(methodID)) {
ThrowException(env, "Invalid class or method");
return nullptr;
}
// 通过内存分配器创建对象
ObjPtr obj = runtime_->GetHeap()->AllocObject(clazz);
if (obj == nullptr) {
// 内存不足处理
ThrowOutOfMemoryError(env);
return nullptr;
}
// 调用构造函数
va_list args;
va_start(args, methodID);
InvokeDirectMethod(runtime_, env, methodID, obj, args);
va_end(args);
return env->ToLocal(obj.Ptr());
}
增加了:
Dalvik通过jdwp
实现调试支持,代码位于dalvik/vm/Jdwp.cpp
:
// dalvik/vm/Jdwp.cpp
void JdwpThread::HandleCommand() {
// 接收调试命令
JdwpPacket packet;
if (!ReceivePacket(&packet)) {
// 接收失败处理
return;
}
// 解析命令类型
switch (packet.GetCommandSet()) {
case JDWP_COMMAND_SET_THREAD:
HandleThreadCommand(packet);
break;
case JDWP_COMMAND_SET_CLASS:
HandleClassCommand(packet);
break;
// 其他命令处理
}
// 发送响应
SendResponsePacket(packet);
}
支持基本的断点调试和线程管理,但缺乏深度性能分析能力。
ART在调试支持上进行了全面升级:
art/runtime/jdwp/jdwp_thread.cc
)// art/runtime/jdwp/jdwp_thread.cc
void JdwpThread::HandleCheckCapabilities(JdwpPacket* packet) {
// 检查调试器权限
if (!HasDebugPermission(packet->GetClientID())) {
SendErrorResponse(packet, JDWP_ERROR_INVALID_ACCESS);
return;
}
// 返回支持的功能列表
SendCapabilitiesResponse(packet);
}
提供更丰富的调试接口和安全机制。
Dalvik主要依赖Linux内核的进程隔离机制,自身安全防护较弱。在权限管理方面,通过AndroidManifest.xml
声明权限,但缺乏运行时检查。垃圾回收机制未对内存安全做特殊处理,存在缓冲区溢出风险。
ART在安全方面进行了大量改进:
art/runtime/selinux/selinux.cc
实现安全上下文管理// art/runtime/selinux/selinux.cc
bool Selinux::CheckAccess(const std::string& type, const std::string& target, const std::string& perm) {
// 调用SELinux内核接口进行权限检查
return selinux_check_access(type.c_str(), target.c_str(), perm.c_str()) == 0;
}
Dalvik为了兼容低配置设备,采用轻量级设计:
ART通过以下方式平衡性能与兼容性:
Dalvik代码结构相对简单,核心模块集中在dalvik
目录下。由于采用JIT编译,编译器实现复杂度较低,但运行时性能优化逻辑分散,维护难度较高。
ART代码规模庞大,涵盖编译、运行时、工具链等多个子系统。AOT编译引入了复杂的中间表示优化和机器码生成逻辑,增加了开发难度。但模块化设计和清晰的架构分层,提高了代码的可维护性和扩展性。
由于设计理念和技术架构限制,Dalvik在以下方面存在瓶颈:
ART持续向以下方向演进: