Android的应用程序开发语言首选的是java,所以Android虚拟机或多或少都会借鉴JVM中的一些设计思想和技术实现。虽然严格意义上讲Android的dalvik/art并不是纯正的java虚拟机(因为没有完全遵循JVM规范),但是本质上跟hotspot等jvm又有很多相似。所以先理解java虚拟机对理解Android虚拟机是会有很大帮助的。
java虚拟机解决的核心问题是:write once,run anywhere,为了这个目标,需要把java语言文件编译为中间状态的字节码bytecode,然后在运行阶段对具体的平台做转换(将字节码翻译成目标平台的机器码)。所以jit,interpreter,内存分配和回收,线程管理等都是为这个目标服务的。
JVM架构中的核心模块:
Java虚拟机的类加载机制,可以参考:JVM类加载
(1)Class loader和双亲委派模型
class loader提供了一种灵活的方式,帮助应用程序将字节码流以自定义的方式加载到虚拟机中。java虚拟机中通常存在几种class loader:
1)Bootstrap classloader
2)Extension classloader
3)System classloader
4)User classloader
Bootstrap classloader是c/c++语言编写的,它所加载的类通常位于JAVA_HOME/lib中,或者有-X bootclasspath指定,属于java的核心实现部分。
Extension classloader负责jre扩展目录中的各种类的加载,如JAVA_HOME/lib/ext目录下的内容。
System classloader 称为应用程序类加载器,用于加载CLASSPATH中的各种类对象,
可以通过classloader.getSystemClassLoader()获取,如果应用程序没有提供自定义的classloader,默认情况下系统将使用system classloader来加载应用程序中的类。
user classloader是应用程序提供,通常继承自java.lang.ClassLoader,并按照规范实现其中的接口。
(2)Runtime Data areas,
Java的内存分配,可以参考:Java内存分配
jvm运行过程中所需的数据区域,包括stack,heap,MethodArea等多个部分。
其中Program counter register区是实现java多线程管理的关键之一,每个java虚拟机线程都需要彼此独立的PC寄存器。如果被执行的是native方法,pc寄存器实际是没有定义的,因为native代码可以被物理硬件直接识别并执行,此时不需要模拟的pc寄存器。
(3)堆和垃圾回收
垃圾收集算法,可以参考:jvm垃圾回收
堆内存的特点是进程独立、线程共享。堆内存最流行的垃圾回收算法是“分代垃圾回收”。
1)Young Generation,年轻代内存,又分为eden,s0,s1,
2)old Generation,老年代内存。
3)Permanent Generation,永久代内存,用于存储和类、方法相关的Meta Data,不属于heap的范围。
绝大多数对象都是在YoungGeneration(年轻代)被分配,也在YoungGeneration被回收。当YoungGeneration空间被填满时,GC会进行Minor Collection(次回收),这次的回收不涉及Heap中的其他Generation。
在Young Generation中,没有被回收的对象被转移到Tenured Generation,然后在Tenured Generation也会被填满,最终触发Major Generation(主回收),这次回收针对整个Heap。
YoungGeneration中幸存的对象被转移到TenuredGeneration(老年代),但是ConcurrentCollector线程在这里进行MajorCollection,而在回收任务结束前空间被耗尽了,这时会触发Full Collections(Full GC)。
因为TenuredGeneration 的majorCollection过程较慢,如果TenuredGeneration空间小于YoungGeneration,会造成频繁的MajorCollection,肯定影响效率。通常serverJVM中,Young Generation 和 Tenured Generation空间比例为1:2,也即是说Young Generation的Eden和Survivor空间之和是整个Heap的1/3,这个比例可以通过-XX:NewRation=n参数来控制。
PermGen,它是JVM用来存储无法在Java语言级描述的对象,这些对象分别是类和方法数据(与class loader有关),以及interned Strings(字符串驻留)。
对于Perm Gen,如果出现OOM,log:java.lang.OutOfMemoryError:PerGen space
实在找不出问题所在,可以使用JVM的-verbose参数,这个参数会在后台打印日志,用来查看那个class loader加载了什么类。
通过MAT工具,Open Query Browser à JavaBasics àClass loader Explorer 在Class loader Explorer面板,可以查看ClassLoader是否加载了过多的类。
什么样的对象会被GC回收?在GC发现通过任何reference chain(引用链)无法访问某个对象的时候,该对象即被回收。
通常GC Roots是一个在current thread(当前线程)的call stack(调用栈)上的对象(如方法参数、局部变量),或者是线程自身,或者是systemclass loader(系统类加载器)加载的类,及nativecode(本地代码)保留的活动对象。
StrongRef(强引用),通常创建的对象都是Strong Ref,于此对应的是强可达性,只有去掉强可达,对象才被回收。
SoftRef(软引用),对应软可达性,只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有Strong Ref时才回收对象。一般可用来实现缓存,通过java.lang.ref.SoftReference实现。
WeakRef(弱引用),比SoftRef更弱,当发现不存在StrongRef时,立即回收对象而不必等到内存吃紧的时候,通过java.lang.ref.WeakReference和java.util.WeakHashMap类实现。
(4)Execution engine,执行引擎是虚拟机中的中枢系统,主流的jvm一般支持多种执行方式:
1)java字节码解释执行,可以参考:jvm 字节码执行引擎
2)在运行阶段根据一定的算法将使用频率高的字节码编译成native code(JIT),然后执行。JIT(just in time)相对于AOT的主要区别是JIT只有在程序运行过程中才会将部分热点代码编译成机器码(这在一定程度上加重了cpu的负担)。
3)在编译阶段将字节码直接转换成机器码,类似于ART虚拟机的AOT技术。
(5)基于寄存器和基于栈的java虚拟机的实现
java虚拟机执行字节码有多种实现方式,用的较多的是基于寄存器和基于栈。
基于栈的方式:
1)单条指令更紧凑,优于JVM执行的是字节码,不需要想基于寄存器的架构那样显性地指定源和目标,所以采用栈实现的方式可以减小单条指令的codesize;
2)可移植性强,基于寄存器的的虚拟机需要考虑很多与目标平台强相关的因素,如要根据硬件平台的真实寄存器的数量、他们的用途等,来实现虚拟机中的寄存器和真实世界中的寄存器之间的“映射关系”。相比来说,栈虚拟机没有这个考虑,所以通用性更好。
3)由于指令是基于栈来操作的,导致完成同一个任务所需的指令数比基于寄存器的架构要多。
4)从执行速度看,基于寄存器的架构更快。因为基于栈的方式需要更多的内存读写操作,而且需要更多的指令数。
ARTVM的框架:
Android虚拟机的核心系统包括:Runtime,ClassLoader system ,Execution Engine System,Heap Manager,GC system,JIT,JNI环境等等。
Runtime,代表了虚拟机的运行时态,起到管理员的作用;
JavaVM,是对虚拟机的抽象,每个进程最多只有一个实例存在;
JNIEnv,负责jni机制,并且是线程相关的。
ClassLoader system,类加载器,又细分:Boot Classloader,system classloader,Dex Classloader等,所有被加载的类及其组成元素由Classlinker统一管理,为了实现类似于elf中的动态延迟加载效果,Android vm使用了dexcache机制;
Execution engine,执行引擎,除了字节码解释执行的方式,还有aot技术。在art中,Interpreter,jit,aot三种执行方式是共存的。
art和dalvik两个vm虽然差别很大,但是对于应用程序来说,没有特别的要求,几乎完全透明,真正的区分的地方是应用程序的安装阶段:
Dalvik,如果当前系统配置的是dalvik,在应用程序安装阶段要对dex文件做odex优化,就是利用dexopt工具来生成odex文件。对于rom中的应用程序,即预装的,优化过程在编译阶段完成;对于第三方应用,在安装时由install系统负责优化,结果保存在/data/Dalvik-cache目录下,后缀是.odex。
art:如果配置的ART,应用安装阶段也有一次预处理,类似dalvik,只是art预处理使用的工具是dex2oat,输出的是linux标准执行文件elf file。
原文链接:https://blog.csdn.net/lin20044140410/article/details/79307964
网友评论