Android虚拟机与类加载机制详情
作者:havenobug??????? 发布时间:2022-12-16 20:09:03
JVM与Dalvik
Android应用程序运行在Dalvik/ART虚拟机,并且每一个应用程序对应有一个单独的Dalvik虚拟机实例。Dalvik虚拟机实则也算是一个Java虚拟机,只不过它执行的不是class文件,而是dex文件。 Dalvik虚拟机与Java虚拟机共享有差不多的特性,差别在于两者执行的指令集是不一样的,前者的指令集是基本寄存器的,而后者的指令集是基于堆栈的。
JMV基于栈,Dalvik基于寄存器
基于栈的虚拟机
对于基于栈的虚拟机来说,每一个运行时的线程,都有一个独立的栈。栈中记录了方法调用的历史,每有一次方法调用,栈中便会多一个栈桢。最顶部的栈桢称作当前栈桢,其代表着当前执行的方法。基于栈的虚拟机通过操作数栈进行所有操作。
字节码指令
在Androidstudio中搜索ASMPlugin,可以直接用这个插件查看字节码
执行过程
基于寄存器的虚拟机
寄存器
寄存器是CPU的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和位址。
基于寄存器的虚拟机
基于寄存器的虚拟机中没有操作数栈,但是有很多虚拟寄存器。其实和操作数栈相同,这些寄存器也存放在运行时栈中,本质上就是一个数组。与JVM相似,在Dalvik VM中每个线程都有自己的PC和调用栈,方法调用的活动记录以帧为单位保存在调用栈上。
与JVM版相比,可以发现Dalvik版程序的指令数明显减少了,数据移动次数也明显减少了。
ART与Dalvik
Dalvik虚拟机执行的是dex字节码,解释执行。从Android 2.2版本开始,支持JIT及时编译(Just In Time)在程序运行的过程中进行选择热点代码(经常执行的代码)进行编译或者优化。 而ART(Android Runtime) 是在 Android 4.4 中引入的一个开发者选项,也是 Android 5.0 及更高版本的默认 Android 运行时。ART虚拟机执行的是本地机器码。Android的运行时从Dalvik虚拟机替换成ART虚拟机,并不要求开发者将自己的应用直接编译成目标机器码,APK仍然是一个包含dex字节码的文件。那么,ART虚拟机执行的本地机器码是从哪里来?
dex2aot
Dalvik下应用在安装的过程,会执行一次优化,将dex字节码进行优化生成odex文件。而Art下将应用的dex字节码翻译成本地机器码的最恰当AOT时机也就发生在应用安装的时候。ART 引入了预先编译机制(Ahead Of Time),在安装时,ART 使用设备自带的 dex2oat 工具来编译应用,dex中的字节码将被编译成本地机器码。
odex的目的:预先提取,减少RAM的占用,因为没有odex的话,系统要从apk包中提取dex再运行。
dexopt与dexaot
dexopt:在Dalvik中虚拟机在加载一个dex文件时,对 dex 文件 进行 验证 和 优化的操作,其对 dex 文件的优化结果变成了 odex(Optimized dex) 文件,这个文件和 dex 文件很像,只是使用了一些优化操作码。
dex2oat:ART 预先编译机制,在安装时对 dex 文件执行AOT 提前编译操作,编译为OAT(实际上是ELF文件)可执行文件(机器码)。
Android N的运作方式
ART 使用预先 (AOT) 编译,并且从 Android N混合使用AOT编译,解释和JIT。 1、最初安装应用时不进行任何 AOT 编译(安装又快了),运行过程中解释执行,对经常执行的方法进行JIT,经过 JIT 编译的方法将会记录到Profile配置文件中。 2、当设备闲置和充电时,编译守护进程会运行,根据Profile文件对常用代码进行 AOT 编译。待下次运行时直接使用。
ClassLoader
介绍
任何一个 Java 程序都是由一个或多个 class 文件组成,在程序运行时,需要将 class 文件加载到 JVM 中才可以使用,负责加载这些 class 文件的就是 Java 的类加载机制。ClassLoader 的作用简单来说就是加载 class 文件,提供给程序运行时使用。每个 Class 对象的内部都有一个 classLoader 字段来标识自己是由哪个ClassLoader 加载的。
ClassLoader是一个抽象类,而它的具体实现类主要有:
BootClassLoader:用于加载Android Framework层class文件。
PathClassLoader:用于Android应用程序类加载器。可以加载指定的dex,以及jar、zip、apk中的classes.dex
DexClassLoader:用于加载指定的dex,以及jar、zip、apk中的classes.dex
很多博客里说PathClassLoader只能加载已安装的apk的dex,其实这说的应该是在dalvik虚拟机上。但现在一般不用关心dalvik了。
Log.e(TAG, "Activity.class 由:" + Activity.class.getClassLoader() +" 加载");
Log.e(TAG, "MainActivity.class 由:" + getClassLoader() +" 加载");
//输出:
Activity.class 由:java.lang.BootClassLoader@d3052a9 加载
MainActivity.class 由:dalvik.system.PathClassLoader[DexPathList[[zip file
"/data/app/com.enjoy.enjoyfix-1/base.apk"],nativeLibraryDirectories=
[/data/app/com.enjoy.enjoyfix-1/lib/x86, /system/lib, /vendor/lib]]] 加载
ClassLoader加载流程与双亲委托机制
可以看到创建 ClassLoader 需要接收一个 ClassLoader parent 参数。这个 parent 的目的就在于实现类加载的双亲委托。即: 某个类加载器在加载类时,首先将加载任务委托给父类加载器,依次递归,如果父 类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务或者没有父类加载器时,才自己去加载。
1、避免重复加载,当父加载器已经加载了该类的时候,就没有必要子ClassLoader再加载一次。 2、安全性考虑,防止核心API库被随意篡改。
因此我们自己创建的ClassLoader: new PathClassLoader("/sdcard/xx.dex", getClassLoader()); 并不仅仅只能加载 xx.dex中的class。
值得注意的是: c = findBootstrapClassOrNull(name); 按照方法名理解,应该是当parent为null时候,也能够加载 BootClassLoader 加载的类。 newPathClassLoader("/sdcard/xx.dex", null) ,能否加载Activity.class? 但是实际上,Android当中的实现为:(Java不同)
类加载
热修复
PathClassLoader 中存在一个Element数组,Element类中存在一个dexFile成员表示dex文件,即:APK中有X个dex,则Element数组就有X个元素。
在 PathClassLoader 中的Element数组为:[patch.dex , classes.dex , classes2.dex]。如果存在Key.class位于patch.dex与classes2.dex中都存在一份,当进行类查找时,循环获得 dexElements 中的DexFile,查找到了Key.class则立即返回,不会再管后续的element中的DexFile是否能加载到Key.class了。
因此实际上,一种热修复实现可以将出现Bug的class单独的制作一份fix.dex文件(补丁包),然后在程序启动时,从服务器下载fix.dex保存到某个路径,再通过fix.dex的文件路径,用其创建 Element 对象,然后将这个 Element 对象插入到我们程序的类加载器 PathClassLoader 的 pathList 中的 dexElements 数组头部。这样在加载出现Bug的class时会优先加载fix.dex中的修复类,从而解决Bug。
来源:https://juejin.cn/post/7139541557245378568
猜你喜欢
- 一、使用在非静态方法上public synchronized void syzDemo(){ System.out.print
- 前言.NET 生态越来越好,初学的朋友也越来越多。处理同一件简单的问题,随着我们知识的积累解决问题的方法也会越来越多。开始学习一门新的语言,
- 本文实例为大家分享了C语言非递归后序遍历二叉树的具体代码,供大家参考,具体内容如下法一:实现思路:一个栈 先按 根->右子树->
- SpringBoot使用protobuf格式的接口建立SpringBoot项目,pom.xml内容如下:<?xml version=&
- 一、引言最近发现很多人在论坛中问到如何防止程序被多次运行的问题的,如: http://social.msdn.microsoft.com/F
- 本文实例为大家分享了Android实现签名涂鸦手写板的具体代码,供大家参考,具体内容如下布局文件<?xml version="
- Lucene提供了一个简单却强大的应用程序接口(API),能够做全文索引和搜寻,在Java开发环境里Lucene是一个成熟的免费开放源代码工
- 用java实现的登录与注册页面,实现了客户端(浏览器)到服务器(Tomcat)再到后端(servlet程序)数据的交互。这里在注册页面加入了
- 本文所述为C#实现根据指定容器和控件名字获得控件的方法,在进行C#应用程序设计时有一定的借鉴价值。分享给大家供大家参考借鉴。具体实现方法如下
- 本文实例讲述了C#中Socket通信用法。分享给大家供大家参考。具体如下:一、UDP方式:服务器端代码:static void Main(s
- 相信很多人都了解c#语言,但是对于c#语言编写应用程序的经验不够多,所以经常为没有实例练习而烦恼吧。今天小编给大家介绍下C#里的多线程技术。
- 本文实例讲述了Spring实战之Bean的作用域singleton和prototype用法。分享给大家供大家参考,具体如下:一 配置<
- 最近有小伙伴告诉我,在循环的判断条件只会计算一次,本金鱼不相信,于是就做了测试,本文记录我做的测试。先来写一个简单的代码, 就一个循环,循环
- 安装JDK 向导进行相关参数设置。如图: 正在安装程序的相关功能,如图: 选择安装的路径,可以自定义,也可以默认路径。如图: 成功安装之
- 前言:最新Servlet 3.0 * 的使用1.pom.xml添加需要使用的依赖<project xmlns="http:/
- 在客户机和服务器之间建立单一的双向连接,这就意味着客户只需要发送一个请求到服务端,那么服务端则会进行处理,处理好后则将其返回给客户端,客户端
- 本文实例讲述了C#显示文件夹下所有图片文件的方法。分享给大家供大家参考。具体实现方法如下:<%@ Page Language=&quo
- 自定义注解+springAop参数非空校验自定义注解,来对对应的方法进行入参校验,为空返回参数错误新建注解类@interface Param
- 配置详情pom.xmldependency> <groupId>com.baomidou<
- 引言热修复技术如今已经不是一个新颖的技术,很多公司都在用,而且像阿里、腾讯等互联网巨头都有自己的热修复框架,像阿里的AndFix采用的是ho