Android 面试原理-Java JVMAndroid Framework

JVM虚拟机

2019-08-10  本文已影响0人  Stan_Z

继续做知识点铺垫。

我这里再简单梳理下虚拟机相关知识点,当然只是热修复与插件化会涉及到的部分。

一、JVM整体结构

java文件先通过编译器生成虚拟机执行的字节码文件,该文件被ClassLoader加载到内存,由虚拟机分区域进行内存管理,然后子系统会执行相关的工作:包括将虚拟机字节码编译为机器码、针对堆内存进行GC等等。同时虚拟机通过本地库接口连接native方法与本地方法库。

1.1 编译流程

class文件生成是通过javac编译器程序执行得到的,整个编译流程简单总结如下:

源代码->词法分析器->Token流->语法分析器->语法树/抽象语法树->语义分析器->
注解抽象语法树->字节码生成器->jvm字节码

这个部分就不管了,了解下点到为止,感兴趣的可以自行研究。

1.2 类加载流程

类加载器介绍:
Bootstrap Loader 负责加载系统类;
Extension ClassLoader 负责加载扩展类(就是继承类和现类);
System ClassLoader 负责加载用户类。

(类加载器Andorid有比较大的区别,但是思想还是一样的)

这里牵涉到几个机制:

如何判断加载的是同一个类:类名相同、包名相同、由相同的ClassLoader加载。

类加载的过程:

image.png
1.3 内存管理
1.3.1 内存区域划分

线程共享区包括:方法区 、java堆。
线程隔离区包括:虚拟机栈、本地方法栈、程序计数器。

1.3.2 虚拟机栈解析

以线程为单位由栈结构来进行管理。栈中对应的栈元素叫栈帧,它是用于支持虚拟机进行方法调用和方法执行的数据结构,每个方法从调用到执行完成就对应一个栈帧在虚拟机栈中入栈到出栈的过程。栈帧包含的内容:局部变量表、栈操作数、动态链接、方法出口。

1.3.3 java堆区解析

java堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。

在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。

简单看下区域的划分以及虚拟机默认分配的比例:

另外还有个永久代,这部分属于方法区,就捎带提一嘴。

那么,这个比例可以配置吗?当然可以,如果玩java服务器的话,肯定要玩玄学调参。参考如下JVM参数选项:

参数名 介绍
-Xms 初始堆大小。如:-Xms256m
-Xmx 最大堆大小。如:-Xmx512m
-Xmn 新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%
-Xss JDK1.5+ 每个线程堆栈大小为 1M,一般来说如果栈不是很深的话, 1M 是绝对够用了的。
-XX:NewRatio 新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3
-XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10
-XX:PermSize 永久代(方法区)的初始大小
-XX:MaxPermSize 永久代(方法区)的最大值
-XX:+PrintGCDetails 打印 GC 信息
-XX:+HeapDumpOnOutOfMemoryError 让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析用

jvm 可配置的参数选项可以参考 Oracle 官方网站给出的相关信息:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

堆内存管理规则:

新生代
新创建的对象先被放在Eden,Eden满了,执行Minor GC,同时把未被 GC 的对象 移动到 S0(from) 或 S1(to) 中。 最后使 S0(from) 、S1(to) 其中一个置为空,这是由GC算法决定的,因为Minor GC对应的是复制算法,因此需要内存交换区,所以多分了from和to这两块区域。

老年代
内存区存放了经过多次 Minor GC 后仍然不能被 GC 的对象。Old区满了,执行Major GC。

另外要补充一点的是:新生代和新生代都可以主动触发 stop-the-world 事件,挂起所有任务,执行 GC 操作。 被挂起的任务只有在 GC 执行完毕后,才会恢复执行。

1.3.4 垃圾回收
垃圾收集算法:
引用类型:
垃圾回收算法:

标记-清除算法(Tracing Collector):遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象。对没标记的对象全部清除。
优点:对不存活对象进行处理,在存活对象高的情况下非常高效。
缺点:清除对象不会整理,造成内存碎片,这部分内存碎片属于内部碎片。

复制算法(Coping Collector): 遍历所有的GC Roots,将可达的对象复制到另一块内存空间,遍历完后清空原来的内存空间(剩下的都是不可达对象)。
优点:对可达对象进行复制,在存活的对象比较少时极为高效。
缺点:需要额外的内存空间。

标记-整理算法(Compacting Collector):在标记-清除算法基础上,增加存活对象内存整理。
优点:不造成内存碎片,也不需要额外内存空间
缺点:整理过程耗时,效率不高。

因此内前面咱们提到的两个GC:
Minor GC: 是发生在新生代中的垃圾收集动作,通常对象存活时间较短,因此采用的是复制算法。
Full GC: 是发生在老年代的垃圾收集动作,通常对象存活时间较长,因此采用的是标记-清除算法(考虑执行效率)/标记-整理算法(考虑内存利用率)。

GC触发时机:

二、JVM与DVM不同

三、ART与DVM相比较的优势

编译优化

DVM使用JIT来将字节码编译为机器码,ART引入AOT预编译。JIT生成的机器码缓存在内存中,优化解释模式的执行,属于运行时优化。而OAT生成的机器码缓存为文件,属于持久化优化,因此下次执行肯定是AOT的方式更快,不用重新生成机器码,直接拿来用。

但是OAT的缺点是每次编译dex2oat CPU占用率非常高,可能造成部分任务抢占不到CPU。另外ART安装时间更长,存储空间占用更大。

垃圾回收优化

改善了Dalvik GC流程,将其非并发过程改变成了部分并发。缩短了任务挂起时间,据官方测试数据说gc效率提高2倍。

内存优化

ART对比DVM 提高了内存使用率,减少了内存碎片化。

之前写的相关文章:
虚拟机(一)-JVM执行java代码流程浅析
虚拟机(二)-Dalvik执行java代码流程浅析
虚拟机(三)-JVM 、DVM 、ART简单对比
虚拟机(四)-JVM垃圾回收

上一篇下一篇

猜你喜欢

热点阅读