1.前言

首要希望觉得本片文章有用的童鞋们记得点赞!重视!收藏!,不要小气你们的称誉啦!

Android的虚拟机通过演变,从Android5.0前的Dalvik虚拟机在Android5.0后全面变成了ART虚拟机,这两个虚拟机都是根据寄存器的,而咱们的JVM是根据栈的。

那为什么安卓的虚拟机是根据寄存器,跟JVM根据栈有所不同呢?最大的原因必定是因为移动端的硬件环境进行了针对性相关优化,比方安卓里边字节码文件是.dex而不class,那正是谷歌对移动端进行了相关针对性的优化,那这个时分就要将JVM根据栈的模式与根据寄存器的模式进行一个比照,咱们才干了解为什么安卓虚拟机会根据寄存器。

2.栈与寄存器的差异

在介绍JVM之前,咱们先了解栈与寄存器的差异是什么?

先上结论!!!

所以为什么Android渠道根据寄存器的规划?

  1. 指令条数少,所以履行的功率更快!
  2. 数据移动次数少、临时成果存放次数少,所以更省内存和履行功率更快!
  3. 映射真实机器的寄存器,所以履行功率更快!

2.1.栈

栈的特点是:FILO(先进后出,Fist In Last Out)

栈的出入口只要一个,那就意味着先进的元素会在栈底,而后进的元素会在栈底,先进的元素不能直接出栈,需求栈顶的元素出栈后,才干出栈,所以栈是一个单向队列的数据结构,咱们先上图来描绘一下栈。

为什么Android平台的虚拟机是基于寄存器设计的?

咱们能够看出,A元素是首要进入栈的,这个时分会被处于栈顶,随着B元素的进入,A元素被压入栈底,而B元素处于栈顶,最后C元素压入栈中,成为了栈顶,这个时分A、B元素出栈都意味着C元素必须要出栈。

Ok,栈的概念仍是比较简单明白的,接下来咱们看看寄存器的概念。

2.2.寄存器

寄存器相当于CPU里边的内存,它本身是有固定的内存地址的,这意味着数据在寄存器中的操作/IO是非常快速的。

寄存器的指令一般较长,在Android里边每个指令占2个字节,可是指令条数会大幅减少,而JVM虚拟站中指令占一个字符,可是指令条数会大幅增加,能快速履行。

那么简单介绍完栈与寄存器的概念之后,咱们就进入下一章,JVM是怎样通过虚拟栈来履行指定的?

3.JVM是怎样通过虚拟栈履行字节码文件的?

3.1 了解JVM运行时的数据区组成

首要咱们要了解JVM运行时的数据区,看一幅图:

为什么Android平台的虚拟机是基于寄存器设计的?

咱们能够由上图看出,ClassLoader将.class文件加载至运行时数据区,运行时数据区其实便是字节码被加载到内存后在内存的一个状况,咱们能够看出有以下几部分。

存放着实例的区域,而GC主要也是针对该区域。

  1. 办法区

存放着从字节码中加载出来的类信息。

  1. 线程独立区域
    1. 程序计数器 保存程序字节码履行的顺序
    2. 虚拟机栈 描绘java办法履行的内存模型
    3. 本地办法栈 本地办法栈则为Native 办法服务

简单了解了运行时数据区的结构后,接下来咱们将深化到JVM中的线程是怎样根据虚拟机栈来履行字节码的。

3.2 了解栈帧

咱们知道虚拟机栈是负责描绘java办法履行的内存模型,而且记载线程内办法的履行状况的,而栈中的元素,也会被称为栈帧,比方咱们现在履行一个main办法,main办法中再履行一个foo办法,那么虚拟机栈中就会存在两个栈帧,如图:

为什么Android平台的虚拟机是基于寄存器设计的?

那如果此刻咱们的虚拟机栈中的foo办法含有相关逻辑,虚拟机栈又是怎样履行这些逻辑的呢?

首要,咱们的虚拟机栈是怎样履行以上字节码的呢?首要咱们了解一下栈帧的组成

为什么Android平台的虚拟机是基于寄存器设计的?

咱们能够看出一个栈帧里边含有

  1. 局部变量表

局部变量表存放着办法中的局部变量+this实例

  1. 操作数

用于字节码履行时,暂存其中间状况的一个内存区域,每个操作数栈的容量是有限的,在编译期会揣度其容量巨细而且记载在字节码文件中,而JVM说的根据栈,其实便是根据该操作数栈。

  1. 办法出口

记载该办法的出口,需求回来什么,回来给谁。

  1. 程序计数器

记载着程序的履行顺序。

3.3 了解栈是怎样履行逻辑的

接下来咱们界说一个foo办法,而且编译成class文件,然后咱们解析一下虚拟机栈是怎样履行字节码程序的。

  public void foo() {
        int a = 1;
        int b = 2;
        int c = (a + b)*9;
    }

然后咱们将其解析成字节码后是这样的

顺序 字节码 助记符
0 0x04 iconst_1
1 0x3b istore_0
2 0x05 iconst_2
3 0x3c istore_1
4 0x1a iload_0
5 0x1b uload_1
6 0x60 iadd
7 0x10 0x09 bipush9
9 0x67 imul
a 0x3d istore_2
b 0xb1 return

那么栈帧中是怎样履行上面的字节码的?

咱们以图解的方式解析全进程,首要是一个栈帧的初始化状况,局部变量表与操作数栈的状况如下:

为什么Android平台的虚拟机是基于寄存器设计的?

咱们能够看出局部变量表中初始化了a,b,c三个变量,而操作数栈中现在容量为2,那么咱们开端履行每一步字节码。

为什么Android平台的虚拟机是基于寄存器设计的?
为什么Android平台的虚拟机是基于寄存器设计的?
为什么Android平台的虚拟机是基于寄存器设计的?
为什么Android平台的虚拟机是基于寄存器设计的?
为什么Android平台的虚拟机是基于寄存器设计的?
为什么Android平台的虚拟机是基于寄存器设计的?
为什么Android平台的虚拟机是基于寄存器设计的?
为什么Android平台的虚拟机是基于寄存器设计的?
为什么Android平台的虚拟机是基于寄存器设计的?

至于最后一行字节码,其实从助记符中能够明显的判断出,办法回来结束了~就不特意贴图了

(从第八张图开端,因为换了分辨率去制图,所以字体变小了,将就看。。)

看完整个履行进程最直观的咱们能看出以下几点:

  1. 字节码大部分时分占1位
  2. 局部变量表中暂存数据,字节码中不负责保存
  3. 操作基本都是根据操作数栈进行操作

既然咱们要作比照,根据栈和根据寄存器的差异是什么?那么接下来就来讲讲寄存器履行逻辑的进程,然后再作一个比照~

4.ART是怎样根据虚拟寄存器履行字节码文件的?

4.1 结构改动

比照根据栈,根据寄存器的虚拟机栈帧构成如下:

为什么Android平台的虚拟机是基于寄存器设计的?

咱们能够看出:

  1. 没有了操作数栈、被虚拟寄存器替代了、
  2. 也没有了变量表
  3. 保留了程序计数器

乍一看如同做了不少减法,直观的感受到履行时占用内存的减少。

ok,了解栈帧中的改动,那么接着看咱们根据寄存器的字节码有哪些改动。

4.2 字节码改动

为什么Android平台的虚拟机是基于寄存器设计的?

咱们能够直观的看出指令数减少了,可是每个字节码从占用一个字节升级到两个字节,接着咱们看看寄存器是怎样履行逻辑的~

4.3 解寄存器是怎样履行逻辑的?

为什么Android平台的虚拟机是基于寄存器设计的?

为什么Android平台的虚拟机是基于寄存器设计的?

为什么Android平台的虚拟机是基于寄存器设计的?

为什么Android平台的虚拟机是基于寄存器设计的?

为什么Android平台的虚拟机是基于寄存器设计的?

最后一步也不贴了,便是return办法~

那么咱们能够明晰的感知到,因为栈帧结构改动,没有了局部变量表,所以字节码中不需求再履行将数据保存到变量表相关的操作,大大的减少了流程,可是咱们看最后一张图,有没有发现少了什么?

办法中 var a的值不见了!

为什么Android平台的虚拟机是基于寄存器设计的?

不要慌,这其实是编译优化,咱们以最少的寄存器为条件,不改动语意,优化掉无用的代码。

ok,了解了他们的栈帧结构、字节码差异、履行逻辑后,咱们进入比照环节!

5.比照环节

直接总结好~上图表!

比照项 根据栈 根据寄存器
字节码单元长度 8位(1字节) 16位(2字节)
单条指令长度 几乎翻倍
相同逻辑指令条数
相同逻辑数据移动次数 尽可能少
相同逻辑临时成果存储次数 尽可能少

所以为什么Android渠道根据寄存器的规划?

  1. 指令条数少,所以履行的功率更快!
  2. 数据移动次数少、临时成果存放次数少,所以更省内存和履行功率更快!
  3. 映射真实机器的寄存器,所以履行功率更快!

以上便是《为什么Android渠道虚拟机根据寄存器的规划?》的全部内容,觉得本片文章有用的童鞋们记得点赞!重视!收藏!,不要小气你们的称誉啦啦啦啦啦