Android里的虚拟机


前言

Android里的虚拟机, 旨在向大家简易叙述虚拟机里的一些知识。

一、虚拟机(jvm)是什么?

  • Java虚拟机(JVM)是Java Virtual Machine的缩写,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能模拟来实现的。Java虚拟机有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。
  • 引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用模式Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。

总的来说,它的作用是把平台无关的.class里的字节码翻译成平台相关的机器码,这样就实现了跨平台运行,如我们Android平台的虚拟机有Dalvik和Art虚拟机。

二、jvm和Android虚拟机

千言万语不及一张图,所以上图!
在这里插入图片描述
从图上可以清楚的分析出

1.JVM虚拟机是以java字节码加载,而Android虚拟机是基于dex加载的。

我们先来看一下class和dex的区别。
参考:Dex文件格式详解
在这里插入图片描述
jar文件里有多个class,而dex文件里只有几个数据区(每个区相当于一个list)。可以看到,当java程序编译成class后,使用dx工具将所有的class文件各个部分(成员变量,方法,常量等)整合到一个dex文件,目的是其中各个类能够共享数据,在一定程度上降低了冗余,同时也是文件结构更加经凑。dex将原来class每个文件都有的共有信息合成一体,这样减少了class的冗余。

dex和class的区别:

  • dex文件减少了整体的文件尺寸,像是一种压缩文件,一个dex文件可以表示更多的class,而class是一种简单单一的文件。
  • Android虚拟机加载类时,只需要一次IO就可以加载很多类,而class需要多次IO,dex文件提高了Android虚拟机的查找速度。
  • dex指令更加密集,class指令多。(下文会分析)
  • dex因为寄存器设计方便寻址,class是虚拟栈需要多次load和store指令(下文会分析)
  • dex适合移动设备,而class适合pc。

2.class文件存在很多冗余的信息,dex工具能把这些冗余信息去除。

int a = 10;
int b = 15;
int c = a + b;

  
  

    虚拟栈(1个字节):
    这段程序执行的顺序肯定是从上往下执行,可以把这段代码看作一个栈,当int a = 10;执行完就出栈了,依次按顺序出栈,且需要更多的指令(load和store),占用比较多的cpu时间。
    寄存器
    可以这么理解 每段代码都会有不同的引用,这样指令可以执行的更加快速,因为毕竟虚拟栈还是一个栈,执行必须按步执行,执行效率低。但因为是引用的关系,我们肯定是要去寻址的(源地址和目标地址),所以这样就导致了指令长度就变成了2~3个字节,这就意味着需要更多的指令空间意味着数据缓冲(d-cache)更容易失效。

    jvm这种没有地址(无变量申明)指令更紧凑,但完成操作需要更多的load/store指令,也意味着更多的指令分派(instruction dispatch)和内存访问次数,访问内存是执行速度的一个重要瓶颈。
    Android虚拟机二地址或三地址指令虽然每条指令占的空间比较多,但总体来说可以用更少的指令完成操作,指令的分派与内存访问次数都比较少。

    3.Android虚拟机基于寄存器,而jvm是基于虚拟栈的。

    那栈到底是什么?程序的执行原理是什么?
    我们首先需要了解同一段代码分别在Android虚拟机和JVM的运行原理。

    public class Demo {
        public static void foo() {
            int a = 1;
            int b = 2;
            int c = a + b;
        }
    }
    
      
      

      我们就看这段代码好了。
      我们用javap命令查看jvm里的代码。在这里插入图片描述
      生成的jvm指令如下

      Classfile /C:/Users/13703/Desktop/Demo.class
        Last modified 2021-1-14; size 356 bytes
        MD5 checksum c88a56df3f58652012d9e23851ab830c
        Compiled from "Demo.java"
      public class Demo
        minor version: 0
        major version: 51
        flags: ACC_PUBLIC, ACC_SUPER
      Constant pool:
         #1 = Methodref          #3.#18         // java/lang/Object."<init>":()V
         #2 = Class              #19            // Demo
         #3 = Class              #20            // java/lang/Object
         #4 = Utf8               <init>
         #5 = Utf8               ()V
         #6 = Utf8               Code
         #7 = Utf8               LineNumberTable
         #8 = Utf8               LocalVariableTable
         #9 = Utf8               this
        #10 = Utf8               LDemo;
        #11 = Utf8               foo
        #12 = Utf8               a
        #13 = Utf8               I
        #14 = Utf8               b
        #15 = Utf8               c
        #16 = Utf8               SourceFile
        #17 = Utf8               Demo.java
        #18 = NameAndType        #4:#5          // "<init>":()V
        #19 = Utf8               Demo
        #20 = Utf8               java/lang/Object
      {
        public Demo();
          descriptor: ()V
          flags: ACC_PUBLIC
          Code:
            stack=1, locals=1, args_size=1
               0: aload_0
               1: invokespecial #1                  // Method java/lang/Object."<init>":()V
               4: return
            LineNumberTable:
              line 1: 0
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0       5     0  this   LDemo;
      

      public static void foo();
      descriptor: ()V
      flags: ACC_PUBLIC, ACC_STATIC
      Code:
      stack=2, locals=3, args_size=0
      0: iconst_1 -int1推至栈顶
      1: istore_0 - 将栈顶int型数值存入本地变量(本地变量表),位置0
      2: iconst_2 -int2推至栈顶
      3: istore_1 - 将栈顶int型数值存入本地变量(本地变量表),位置1
      4: iload_0 - 将本地变量第0int型推送至栈顶
      5: iload_1 - 将本地变量第1int型推送至栈顶
      6: iadd - 将栈顶两int型数值相加并将结果压入栈顶
      7: istore_2 - 将栈顶int型数值存入本地变量(本地变量表),位置2
      8: return - 从当前方法返回void
      LineNumberTable:
      line 3: 0
      line 4: 2
      line 5: 4
      line 6: 8
      LocalVariableTable:
      Start Length Slot Name Signature
      2 7 0 a I
      4 5 1 b I
      8 1 2 c I
      }
      SourceFile: “Demo.java”

        所以jvm指令有很多的load/store。

        我们再来生成android虚拟机里的指令。
        我们需要借助Andrid sdk 里的 build-tools下的任意一个版本的dx.bat工具。
        在这里插入图片描述

        Demo.foo:()V:
        regs: 0003; ins: 0000; outs: 0000
          0000: code-address
          0000: local-snapshot
          0000: code-address
          0000: code-address
          0000: local-snapshot
          0000: const/4 v0, #int 1 // #1     ---------------------   申明一个变量v0 (4个字节) 赋值1
          0001: local-start v0 "a": int     
          0001: const/4 v1, #int 2 // #2     ---------------------    申明一个变量v0(4个字节)赋值2
          0002: local-start v1 "b": int
          0002: add-int v2, v0, v1           ---------------------     把v0+v1赋值给v2
          0004: local-start v2 "c": int        
          0004: code-address
          0004: code-address
          0004: local-snapshot
                  v0 "a": int
                  v1 "b": int
                  v2 "c": int
          0004: return-void					---------------------     返回空
          0005: code-address               
          debug info
            line_start: 3
            parameters_size: 0000
            0000: prologue end
            0000: line 3
            0001: line 4
            0001: +local v0 a int
            0002: line 5
            0002: +local v1 b int
            0004: line 6
            0004: +local v2 c int
            end sequence
          source file: "Demo.java"
        

          很明显 ,我们可以从视觉的感觉到Android虚拟机里的指令(arm指令)少的多了。
          后面添加了-----------------这个为有用的代码,其他都是dex工具自动给我们生成的代码。

          而Android虚拟机又有分别:

          • Dalvik虚拟机
            使用JIT(Just In Time),每它实时的将一部份就把dex字节码翻译成机器码,所以安装的时候比较快,但我们每次都要编译加运行,这样会拖慢应用以后启动的效率。
          • Art虚拟机
            使用AOT(Ahead of Time),故名思意,在应用安装的期间,就把dex字节码翻译成机器码存在设备上,所以应用占用的空间会有所增大,但这样应用程序每次运行就不用重复编译了,启动就快了,从而减少了cpu的使用,改善了电池的续航。

          总结

          1.1.1Jvm,Dalvik与Art三者之间的区别

          1.2.1 JVM虚拟机与Android虚拟机区别

          Android虚拟机执行的是.dex格式文件 jvm执行的是.class文件

          class文件存在很多的冗余信息,dex工具会去除冗余信息

          Android虚拟机是基于寄存器的虚拟机 而jvm执行是基于虚拟栈的虚拟机

          1.2.3 Art虚拟机与Dalvik虚拟机区别

          1. Dalvik下,应用每次运行都需要通过即时编译器(JIT)将字节码转换为机器码,即每次都要编译加运行,这虽然会使安装过程比较快,但是会拖慢应用以后每次启动的效率。

          2. 而在ART 环境中,应用在第一次安装的时候,字节码就会预编译(AOT)成机器码,这样的话,虽然设备和应用的首次启动(安装慢了)会变慢,但是以后每次启动执行的时候,都可以直接运行,因此运行效率会提高。

            典型的 空间换时间 128G —>apk

          3. ART占用空间比Dalvik大(字节码变为机器码之后,可能会增加10%-20%),这也是著名的“空间换时间大法"。

          4. Art预编译也可以明显改善电池续航,因为应用程序每次运行时不用重复编译了,从而减少了 CPU 的使用频率,降低了能耗。

          1.2.1那dex和class到底在结构上的区别
          1. dex文件减少整体的文件尺寸 dex更像是一种压缩文件,一次可以表示更多的class。class像是一种单个文件
          2. Android虚拟机加载类时 只对dex需要一次IO可以加载很多新类,而class需要加载多次IO,Android虚拟机提高查找速度
          3. dex指令更加密集 。class指令比较多
          4. dex 寄存器设计方便寻址,class java栈需要更多次load与store指令
          5. dex适合于移动设备,性能不太高的要求。class适合PC大内存,单指令小的情况下可以快速执行
          1.4.1 Android虚拟机中寄存器起什么作用,与栈的区别在哪里(又或者基于栈与基于寄存器的架构,谁更快?)

          原因是:虽然没有地址(无变量声明)指令更紧凑,但完成操作需要更多的load/store指令,也意味着更多的指令分派(instruction dispatch)次数与内存访问次数;访问内存是执行速度的一个重要瓶颈,二地址或三地址指令虽然每条指令占的空间较多,但总体来说可以用更少的指令完成操作,指令分派与内存访问次数都较少。

          1.5.1Arm指令究竟是什么指令,与字节码指令的区别

          字节码指令 和 Arm指令内容是不一样

          如 同样一个 a+b

          在 jvm的指令 iadd idiv imul

          但是在dalvik指令是 add-int mul-int

          arm指令是由arm公司开发的。 指令含有地址,而字节码指令没有地址

          字节码指令是 sun公司开发,简单高效

          1.6.1为什么Art虚拟机比Dalvik虚拟机运行速度高

          (1)在Dalvik下,应用每次运行都需要通过即时编译器(JIT)将字节码转换为机器码,即每次都要编译加运行,这虽然会使安装过程比较快,但是会拖慢应用以后每次启动的效率。而在ART 环境中,应用在第一次安装的时候,字节码就会预编译(AOT)成机器码,这样的话,虽然设备和应用的首次启动(安装慢了)会变慢,但是以后每次启动执行的时候,都可以直接运行,因此运行效率会提高。

          (2)ART占用空间比Dalvik大(字节码变为机器码之后,可能会增加10%-20%),这也是著名的“空间换时间大法"。

          (3)预编译也可以明显改善电池续航,因为应用程序每次运行时不用重复编译了,从而减少了 CPU 的使用频率,降低了能耗。

          ARM指令集

          系统性能的显著提升

          应用启动更快、运行更快、体验更流畅、触感反馈更及时

          更长的电池续航能力

          支持更低的硬件

          转载: https://blog.csdn.net/sunlifeall/article/details/112489978

          • 0
            点赞
          • 7
            收藏
            觉得还不错? 一键收藏
          • 0
            评论

          “相关推荐”对你有帮助么?

          • 非常没帮助
          • 没帮助
          • 一般
          • 有帮助
          • 非常有帮助
          提交
          评论
          添加红包

          请填写红包祝福语或标题

          红包个数最小为10个

          红包金额最低5元

          当前余额3.43前往充值 >
          需支付:10.00
          成就一亿技术人!
          领取后你会自动成为博主和红包主的粉丝 规则
          hope_wisdom
          发出的红包
          实付
          使用余额支付
          点击重新获取
          扫码支付
          钱包余额 0

          抵扣说明:

          1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
          2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

          余额充值