热修复与插件化基础——Java与Android虚拟机

4,221 阅读6分钟

一、Java虚拟机(JVM)

1、JVM整体结构

  1. 使用javac将java文件编译成class文件。
  2. 类加载器(ClassLoader)将class字节码加载进JVM对应的内存中。
  3. JVM将内存分配给方法区、堆区、栈区、本地方式栈4个部分,这4个部分分别存储字节码不同的部分。
  4. 垃圾回收器(gc)会管理整个内存空间中的垃圾。

2、Java代码的编译和执行过程

下图是Java代码编译的详细流程(即,javac的执行过程),了解即可,一般只要知道java文件是通过javac命令编译成class文件,再通过java命令运行的就可以了,如:

javac Hello.java
java Hello

3、类加载器

1)Java中的类加载器

2)加载流程

  • Loading:类的信息从文件中获取并载入到JVM的内存中。
  • Verifying:检查读入的结构是否符合JVM规范的描述。
  • Preparing:分配一个结构用来存储类信息。
  • Resolving:把类的常量池中的所有符号引用变成直接引用。
  • Initializing:执行静态初始化程序,把静态变量初始化成指定的值。

4、内存管理

java中的内存管理指的是下图中“内存空间”部分的内存操作。

1)Java栈区:

作用:存放java方法执行时所有的数据。 组成:由栈帧组成,一个栈帧代表一个方法的执行。

Java栈帧:每个方法从调用到执行完成就对应一个栈帧在虚拟机栈中入栈到出栈。它描述了一个方法的局部变量表、栈操作数、动态链接、方法出口。

2)本地方法栈

与Java栈区基于一致。

作用:本地方法栈是专门为native方法服务的。

3)方法区

存储被虚拟机加载的类信息、常量、静态常量、即时编译器编译后等数据(这些数据在程序启动后会永远占据内存)。

4)堆区

作用:所有通过new创建的对象的内存都在堆中分配。 特点:是虚拟机中最大的一块内存,是GC要回收的部分。

对于堆区,其内存结构还有些不一样的地方,先看下图:

简单来说,堆区分为新生代(Young Generation)与老年代(Old Generation),程序在创始对象时,对象会先被分配到新生代中,当新生代区内存不足时,JVM会通过一定的算法规则将新生代中的对象转移至老年代中,当新生代与老年代都没有足够的内存空间时,JVM就会抛出OOM异常。

5、垃圾回收

1、垃圾收集算法

1)引用记数算法(jdk1.2之前)

在内存创建对象的同时,会为它创建一个引用记数器,并将引用记数器加1,每次有引用引用到此对象时,记数器就会累计加1,而当其中一个引用销毁时,记数器就会减1,当引用记数器为0时,说明该对象已经是垃圾对象,下次gc时,对象就会被回收了。

弊端: 对象A与对象B互相引用时,这2个对象的引用记数器永远是正数,当这2个对象都没有被其他对象所引用时(对象不可达),会因为它们的引用记数器不为0导致它们不会被gc回收。

2)可达性算法(jdk1.2 +)

也称为根搜索算法。把程序所有的引用关系看做是一张图(有向图),从GC Root节点开始寻找所有的引用节点,当所有的引用节点寻找完毕之后,剩余的节点被认为是没有引用的节点,即不可达的节点,就是垃圾对象。

上图中ObjD、E、F因为没有路径可达,所以是垃圾对象。

2、引用的类型

java中的引用类型有4种:强引用、软引用、弱引用、虚引用。其中,强引用和弱引用在开发中最常用。

弱引用的创建

// 强引用
Object obj = new Object();
// 弱引用,此时obj与wf都引用了Object对象
WeakReference<Object> wf = new WeakReference<Object>(obj);
// 断开强引用,此时只有wf引用这个Object对象
obj = null;
// 通用弱引用获取Object对象(可能为null)
wf.get();

在使用wf.get()时,要判断获取到的对象是否为null,因为弱引用不会阻止对象的回收。

3、垃圾回收算法

1)标记-清除算法

从根集合遍历所有的引用,上图中,根集合引用了A,A引用了C,B是不可达的对象引用,在扫描阶段中,B会被标记为垃圾对象,当垃圾回收机制执行时,会直接将B对象置为空,此时内存块中就只剩下A、C对象引用,B就被垃圾回收给回收掉了。 优点:不需要进行对象的移动,仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效。 缺点:由于标记-清除算法会直接回收掉不存活的对象,会造成内在碎片,不利于后续对象的分配

2)复制算法

从根集合开始遍历,上图中,遍历到A时是可达的,就把A复制到另一块空闲的内存中,继续遍历,发现B不可达,直接跳过,往后,发现C可达,就把C同样地复制到这块空闲内存中,等所有复制都处理完时,把原来的内存空间清空,只保留复制后的这块内存空间。 优点:当存活对象比较少时,极为高效,且不会有产生内存碎片。 缺点:需要一块内存作为交换空间来进行对象的移动。

3)标记-整理算法

从根集合开始遍历,通过对整个内存区的扫描,将可回收对象扫描出来,上图中,到了第二阶段,就将B标记为可回收对象,到了第三个阶段,直接扫描并消除内存中被标记的对象,同时,在回收不存活对象占用的空间时,会将内存中所有存活对象往左端空闲处移动,并更新对应的指针。

这三种算法各有优劣,JVM在处理垃圾时会整合去使用,并不是只使用其中某个算法。当内存中存活的对象比较少时,采用“复制算法”去处理垃圾对象;当内存中存活的对象比较多时,会采用“标记-整理算法”或“标记-清除算法”去处理垃圾对象。

4、触发回收

  • Java虚拟机无法再为新对象分配内存空间了
  • 手动调用System.gc()方法(强烈不推荐,不会马上执行,却会加大虚拟机压力)
  • 低优先级的GC线程,被启动了。

二、Android 虚拟机

1、Dalvik VM 与 JVM 的不同

  • 执行的文件不同,一个是class,一个是dex。
  • 类加载的系统与JVM区别较大。
  • 可以同时存在多个DVM,但JVM只能存在一个。
  • Dalvik是基于寄存器的,而JVM是基于栈的。

2、Dalvik VM 与 ART 的不同

  • DVM使用JIT来将字节码转换成机器码,效率低。
  • ART采用了AOT预编译技术,执行速度更快。
  • ART会占用更多的应用安装时间和存储空间。

欢迎关注微信公众号:全栈行动