赞
踩
总的来说,它的作用是把平台无关的.class里的字节码翻译成平台相关的机器码,这样就实现了跨平台运行,如我们Android平台的虚拟机有Dalvik和Art虚拟机。
千言万语不及一张图,所以上图!
从图上可以清楚的分析出
我们先来看一下class和dex的区别。
参考:Dex文件格式详解
jar文件里有多个class,而dex文件里只有几个数据区(每个区相当于一个list)。可以看到,当java程序编译成class后,使用dx工具将所有的class文件各个部分(成员变量,方法,常量等)整合到一个dex文件,目的是其中各个类能够共享数据,在一定程度上降低了冗余,同时也是文件结构更加经凑。dex将原来class每个文件都有的共有信息合成一体,这样减少了class的冗余。
dex和class的区别:
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 ––––––––––- 将int型1推至栈顶
1: istore_0 ––––––––––- 将栈顶int型数值存入本地变量(本地变量表),位置0
2: iconst_2 ––––––––––- 将int型2推至栈顶
3: istore_1 ––––––––––- 将栈顶int型数值存入本地变量(本地变量表),位置1
4: iload_0 ––––––––––- 将本地变量第0个int型推送至栈顶
5: iload_1 ––––––––––- 将本地变量第1个int型推送至栈顶
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虚拟机又有分别:
1.2.1 JVM虚拟机与Android虚拟机区别
Android虚拟机执行的是.dex格式文件 jvm执行的是.class文件
class文件存在很多的冗余信息,dex工具会去除冗余信息
Android虚拟机是基于寄存器的虚拟机 而jvm执行是基于虚拟栈的虚拟机
1.2.3 Art虚拟机与Dalvik虚拟机区别
Dalvik下,应用每次运行都需要通过即时编译器(JIT)将字节码转换为机器码,即每次都要编译加运行,这虽然会使安装过程比较快,但是会拖慢应用以后每次启动的效率。
而在ART 环境中,应用在第一次安装的时候,字节码就会预编译(AOT)成机器码,这样的话,虽然设备和应用的首次启动(安装慢了)会变慢,但是以后每次启动执行的时候,都可以直接运行,因此运行效率会提高。
典型的 空间换时间 128G —>apk
ART占用空间比Dalvik大(字节码变为机器码之后,可能会增加10%-20%),这也是著名的“空间换时间大法"。
Art预编译也可以明显改善电池续航,因为应用程序每次运行时不用重复编译了,从而减少了 CPU 的使用频率,降低了能耗。
- dex文件减少整体的文件尺寸 dex更像是一种压缩文件,一次可以表示更多的class。class像是一种单个文件
- Android虚拟机加载类时 只对dex需要一次IO可以加载很多新类,而class需要加载多次IO,Android虚拟机提高查找速度
- dex指令更加密集 。class指令比较多
- dex 寄存器设计方便寻址,class java栈需要更多次load与store指令
- dex适合于移动设备,性能不太高的要求。class适合PC大内存,单指令小的情况下可以快速执行
原因是:虽然没有地址(无变量声明)指令更紧凑,但完成操作需要更多的load/store指令,也意味着更多的指令分派(instruction dispatch)次数与内存访问次数;访问内存是执行速度的一个重要瓶颈,二地址或三地址指令虽然每条指令占的空间较多,但总体来说可以用更少的指令完成操作,指令分派与内存访问次数都较少。
字节码指令 和 Arm指令内容是不一样
如 同样一个 a+b
在 jvm的指令 iadd idiv imul
但是在dalvik指令是 add-int mul-int
arm指令是由arm公司开发的。 指令含有地址,而字节码指令没有地址
字节码指令是 sun公司开发,简单高效
(1)在Dalvik下,应用每次运行都需要通过即时编译器(JIT)将字节码转换为机器码,即每次都要编译加运行,这虽然会使安装过程比较快,但是会拖慢应用以后每次启动的效率。而在ART 环境中,应用在第一次安装的时候,字节码就会预编译(AOT)成机器码,这样的话,虽然设备和应用的首次启动(安装慢了)会变慢,但是以后每次启动执行的时候,都可以直接运行,因此运行效率会提高。
(2)ART占用空间比Dalvik大(字节码变为机器码之后,可能会增加10%-20%),这也是著名的“空间换时间大法"。
(3)预编译也可以明显改善电池续航,因为应用程序每次运行时不用重复编译了,从而减少了 CPU 的使用频率,降低了能耗。
ARM指令集
系统性能的显著提升
应用启动更快、运行更快、体验更流畅、触感反馈更及时
更长的电池续航能力
支持更低的硬件
转载: https://blog.csdn.net/sunlifeall/article/details/112489978
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。