5分钟了解Android虚拟机Dalvik和ART,让手机告别卡顿!

一.Dalvik虚拟机

        Dalvik虚拟机(DVM)是Google专门为Android平台开发的虚拟机。它运行在Android运行时库中。其名字来源于作者Bornstein的祖先居住过的名为Dalvik的小渔村。

1.DVM与JVM的主要区别

        DVM的设计没有遵循JVM规范来实现,这也是后来导致Google被Oracle起诉的原因。

1)架构不同

        JVM基于栈,DVM基于寄存器。

2)执行字节码不同

        JVM:.java文件->.class文件->.jar文件
        DVM:.java文件->.class文件->.dex文件->.apk文件
        DVM通过dx工具将所有的class文件整合成一个dex文件,并去掉其中冗余的信息。

3)DVM的预加载-共享机制

        不同应用运行时可以共享相同的类,JVM不同的程序是独立的。

2.DVM的运行时堆

        DVM运行时使用标记——清除算法进行GC。由两个Space和多个辅助数据结构组成。两个Space为Zygote Space(Zygote Heap)和Allocation Space(Active Heap)。

1)Zygote Space

        用来管理Zygote进程在启动过程中预加载和创建的各种对象。Zygote Space不会触发GC。Zygote进程和应用程序进程间共享Zygote Space。
        在Zygote进程fork第一个子进程之前,会将Zygote Space分成两部分,原来被使用的部分仍未Zygote Space,而未使用的部分为Allocation Space。以后的对象都在Allocation Space上进行分配和释放。Allocation Space在每个进程中都独立拥有一份,且进程间不共享。

2)Card Table

        用于DVM Concurrent GC,当第一次进行垃圾标记后,记录垃圾信息。

3)Heap Bitmap

        有两个Heap Bitmap,一个用来记录上次GC存活的对象,另一个用来记录这次GC存活的对象。

4)Mark Stack

        在GC标记阶段使用,用来遍历存活的对象。

3.DVM的GC日志

1)格式

        D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>, <Pause_time>
参数:
        GC_Reason:引起GC的原因。
        Amount_freed:本次GC释放内存的大小。
        Heap_stats:堆的空闲内存百分比 (已用内存/堆的总内存)。
        External_memory_stats:API小于等于级别10的内存分配 (已分配的内存/引起GC的阈值)。
        Pause_time:暂停时间。并发暂停时间有两个:一个在垃圾收集开始,一个在垃圾收集完成。

2)引起GC的原因

        i)GC_CONCURRENT:当堆开始填充时,并发GC可以释放内存。
        ii)GC_FOR_MALLOC:当堆内存已满时,App尝试分配内存而引起的GC,系统必须停止App并回收内存。
        iii)GC_HPROF_DUMP_HEAP:当用户请求创建HPROF文件来分析堆内存时出现的GC。
        iv)GC_EXPLICIT:显式GC,例如调用System.gc();。
        v)GC_EXTERNAL_ALLOC:仅适用于API级别小于等于10,且用于外部分配内存的GC。

二.ART虚拟机

        Android4.4后用来替换Dalvik虚拟机。

1.ART和DVM的区别

1)DVM采用JIT编译,ART采用AOT编译。

        JIT编译:Just In Time,DVM中的应用每次运行时,JIT编译器将字节码转换成机器码。
        AOT编译:Ahead Of Time,ART中系统在安装应用程序时会事先将字节码编译成机器码并存储在本地。

2)DVM为32位CPU设计,ART支持64位并兼容32位CPU。

3)ART对垃圾回收机制进行了改进。

4)ART的运行时堆划分与DVM不同。

2.ART的运行时堆

        ART采用多种垃圾收集方案,每个方案运行不同的垃圾收集器。对于不同的方案,ART的运行时堆划分不同。默认方案为CMS(并发标记清除)方案,使用sticky-CMS和partial-CMS。默认的ART运行时堆由4个Space和多个辅助数据结构组成。4个Space分别为Zygote Space、Allocation Space、Image Space、Large Object Space。

1)Zygote Space

        与DVM相同,进程间共享。

2)Allocation Space

        与DVM相同。

3)Image Space

        用来存放一些预加载类,进程间共享。

4)Large Object Space

        用来分配一些大对象,默认大小为12KB。

5)其它

        ART的Java堆还包含两个Mod Union Table,一个Card Table,两个Heap Bitmap,两个Object Map,以及三个Object Stack。

3.ART的GC日志

        ART会为主动请求的垃圾收集事件或者认为GC速度慢时才会打印GC日志。GC速度慢是指GC暂停超过5ms或者GC持续时间超过100ms。

1)格式

        I/art: <GC_Reason> <GC_Name> <Objects_freed> (<Size_freed>) AllocSpace Objects, <Large_objects_freed> (<Large_object_size_freed>) <Heap_stats> LOS objects, <Pause_time(s)>
参数:
        GC_Reason:引起GC的原因。
        GC_Name:垃圾收集器的名称。
        Objects_freed:本次GC从非Large Object Space中回收的对象的数量。
        Size_freed:本次GC从非Large Object Space中回收的字节数。
        Large_objects_freed:本次GC从Large Object Space中回收的对象数量。
        Large_object_size_freed:本次GC从Large Object Space中回收的字节数。
        Heap_stats:堆的空闲内存百分比,即(已用内存/堆的总内存)。
        Pause_time(s):暂停时间。ART只暂停一次,出现在GC结束。

2)引起GC的原因

        i)Concurrent:并发GC,不会使App的线程暂停,该GC在后台线程运行,不会阻止内存分配。
        ii)Alloc:当堆内存已满时,App尝试分配内存引起的GC,这个GC会发生在正在分配内存的线程中。
        iii)Explicit:App显式的请求垃圾回收,例如调用System.gc()。
        iv)NativeAlloc:Native内存分配时,触发的GC。
        v)CollectorTransition:由堆转换引起的回收,运行时切换GC引起的。将所有对象从空闲列表空间复制到碰撞指针空间,反之亦然。仅出现在内存较小的设备上App将进程从可察觉的暂停状态更改为可察觉的非暂停状态。
        vi)HomogeneousSpaceCompact:齐性空间压缩是指空闲列表到压缩的空闲列表空间,通常发生在App移动到可察觉的暂停进程状态。以此来减小内存使用并对堆内存进行碎片整理。
        vii)DisableMovingGc:不是真正触发GC的原因。发生并发堆压缩时,由于使用了GetPrimitiveArrayCritical,收集会被阻塞。
        viii)HeapTrim:不是触发GC的原因。收集会一直被阻塞,直到堆内存整理完毕。

3)垃圾收集器的名称:

        i)Concurrent Mark Sweep(CMS):CMS收集器是一种以获取最短收集暂停时间为目标的收集器,采用标记-清除算法实现。它是完整的堆垃圾收集器,能释放除了Image Space外的所有空间。
        ii)Concurrent Partial Mark Sweep:部分完整的堆垃圾收集器,能释放除了Image Space和Zygote Space外的所有空间。
        iii)Concurrent Sticky Mark Sweep:粘性收集器,基于分代的垃圾收集思想,只能释放自上次GC以来分配的对象。它比一个完整的或部分完整的垃圾收集器扫描的更频繁,因为它更快且暂停时间更短。
        iv)Marksweep + Semispace:非并发的GC,复制GC用于堆转换以及齐性空间的压缩。

三.虚拟机的启动

app_main.cpp中main函数的执行过程:

        判断若为Zygote进程,则调用AppRuntime对象的start函数启动Zygote进程,内部调用startVm函数启动Java虚拟机。

AndroidRuntime.cpp中start函数的执行过程:

1.调用JniInvocation对象的Init函数,初始化虚拟机环境。
        1)调用GetLibrary函数,获取系统的虚拟机类型,libart.so或libdvm.so。
        2)调用dlopen函数加载libart.so或libdvm.so。
2.调用startVm函数,启动虚拟机。
3.调用startReg函数,为虚拟机注册JNI方法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值