Android 面试系统复习系列(三)Java 虚拟机原理
运行时数据区
程序计数器(线程私有)
它可以看做当前线程所执行的字节码的行号指示器。
虚拟机栈(线程私有)
虚拟机栈描述的是 Java 方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完成的过程,就对应着一个栈帧再虚拟机栈中入栈到出栈的过程。
本地方法栈(线程私有)
同虚拟机栈,区别就是虚拟机栈执行的是 Java 方法,本地方法栈执行的是 Native 的方法。
Java 堆(线程共享)
存放对象实例,几乎所有对象实例都在这里分配内存,也是垃圾收集器管理的主要区域。
方法区(线程共享)
存储已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。
垃圾回收机制
Java 虚拟机如何判断一个对象可以回收
有两种主流算法,一种是引用计数法,给对象添加一个引用计数器,每当有一个地方引用它时,计数器加一,引用失效时计数器减一。
这种算法简单高效,但是却避免不了互相循环引用无法回收的问题,所以 Java 虚拟机并没有采用这种算法。
所以我们 Java 虚拟机采用的是第二种算法,可达性分析算法。
这个算法的基本思路就是通过一系列的成为“GC Roots”的对象作为七点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到 GC Roots没有任何引用链相连时,则证明此对象是不可达的,所以会被虚拟机判定为可回收的对象。
可作为 GC Roots 的对象
- 虚拟机栈中引用的对象
- 方法去中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中 JNI 引用的对象
垃圾回收算法
标记-清除算法
最基础的垃圾回收算法,先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
优点:实现简单
缺点:一、效率低,标记和清除两个过程效率都不高。二、回收后可能产生大量不连续的内存碎片,空间碎片太多可能会导致以后在分配大内存对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集。
复制算法
将内存按容量划分为大小相同的两块,然后每次只使用其中的一块。当一块内容用完了,就将还存活的对象移动到另一块,然后再将之前使用的内存空间一次性清理掉。
优点:实现简单,运行高效
缺点:会将可用内存缩小为原来的一半,对象存活率高的话,效率会变低
现在商业虚拟机在回收新生代区域的时候都会采用这种算法,不过由于新生代里的对象绝大部分都是“朝生夕死”,所以并不需要按照 1:1 的比例来划分内存空间,HotSpot 虚拟机默认比例是 8:1,所以空闲出来的内存空间很小。不过在存活对象大于剩余的空间时,会向老年代进行分配担保,直接进入老年代。
标记-整理算法
标记的过程同标记-清除算法的标记过程,不过后续不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后再清理边界外的内存。
老年代因为对象存活率高,没有额外空间对它分配担保,所以一般使用标记-清理或者标记-整理算法进行回收。
分代回收算法
当前商业虚拟机的路基手机都采用这种算法,并没有什么新的思想,只是根据对象的存活周期的不同,将内存分为几块,一般是新生代,老年代,然后再根据其区域的特点,采用不同的垃圾收集算法进行回收。
Dalvik 虚拟机和 JVM 的区别
掘金上这篇文章写得很清晰 https://juejin.im/post/59b7fa8cf265da066d3323bb
这里摘几点主要的记一下:
- Dalvik 基于寄存器,基于寄存器的虚拟机虽然比基于堆栈的虚拟机在硬件,通用性上要差一些,但是它的代码执行效率去更好
- JVM 基于栈
- Dalvik虚拟机运行的是其专有的文件格式Dex
- JVM 运行 java 字节码
ART 虚拟机
ART 的机制与 Dalvik 不同。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(just in time ,JIT)转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。
缺点:
1.机器码占用的存储空间更大,字节码变为机器码之后,可能会增加10%-20%(不过在应用包中,可执行的代码常常只是一部分。比如最新的 Google+ APK 是 28.3 MB,但是代码只有 6.9 MB。)
2.应用的安装时间会变长。
网友评论