Android---DVM以及ART对JVM进行优化

2023-10-15 10:52

本文主要是介绍Android---DVM以及ART对JVM进行优化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Dalvik 

Dalvik 是 Google 公司自己设计用于 Android 平台的 Java 虚拟机,Android 工程师编写的 Java 或者 Kotlin 代码最终都是在这台虚拟机中被执行的。在 Android 5.0 之前叫作 DVM,5.0 之后改为 ART(Android Runtime)。在整个 Android 操作系统中,ART 位于图中红框位置。

虚拟机必须符合 Java 虚拟机规范,也就是要通过 JCM(Java Compliance Kit)的测试并获得授权。但是 DVM/ART 并没有得到授权。DVM 大多数实现与传统的 JVM 相同

\bullet Android 最初是被设计用于手机端,对内存空间要求较高;

\bullet 起初 Dalvik 目标是只运行在 ARM 架构的 CPU 上。 

针对这几种情况,Android DVM 有了自己独有的优化措施。

Dex 文件

传统 Class 文件是由一个 Java 源码文件生成的 .class 文件。Android 是把所有 Class 文件进行合并优化,然后生成一个最终的 class.dex 文件。dex 文件去除了 class 文件中的冗余信息(比如重复字符常量),并且结构更加紧凑。因此在 dex 解析阶段,可以减少 I/O 操作,提高了类的查找速度。

实例演示

1. 创建2个 java 类 Dex1.java 和 Dex2.java

public class Dex1 {private int num = 1;public int add(int i, int j) {return i + j;}
}
public class Dex4 {private int num = 1;private int count = 0;public int add(int i) {return i + num;}
}

 2. 通过 javac 命令将它们编译为 .class 文件

javac Dex1.java
javac Dex2.java

3. 通过以下命令将 Dex1.class 和 Dex2.class 打包到一个 jar 文件中

jar cvf AllDex.jar Dex1.class Dex2.class

4. 使用 dx 命令将 AllDex.jar 进行优化,并生成 AllDex.dex 文件

dx --dex --output allDex.dex AllDex.jar

注意: dx 命令需要配置环境变量中。

5. 通过 Android SDK 中的工具 dexdump 查看其字节码

dexdump -d -l plain AllDex.dex

结果如下

架构基于寄存器&基于堆栈结构

JVM 指令集是基于栈结构来执行的,Android 是基于寄存器的是在内存中模拟一组寄存器。Android 的字节码(smali)更多的是二地址指令和三地址指令。

DVM 字节码和 JVM 字节码的区别,如下代码示例:

public int add(int i, int j){return i +j;
}

1. 编译为 Dex1.class 文件后,add 方法的字节码如下,通过4行指令完成

2. 通过 dx 命令将 Dex1.class 优化为 .dex 文件后,再次查看它的 Dalvik 字节码。

add-int 指令需要3个寄存器参数:v0、v1、v2,这个指令会将 v2 和 v3 进行相加运算,然后将结果保存在寄存器 v0 中。return 指令将结果返回。

Dalvik 通过2行指令完成。基于寄存器的指令比基于栈的指令少,但基于寄存器的指令更长。二者比较如下:

内存管理与回收

DVM 和 JVM 另一个显著的不同就是内存结构的区别,主要体现在对堆内存的管理。Dalvik 虚拟机中将堆内存划分为两部分:Active HeanpZygote Heap。如下图所示

 图中的 Card Table 和 两个 Heap Bitmap 主要用来记录垃圾收集过程中对象的引用情况。

为什么要分 Zygote 和 Active 两部分?

Android 系统中的第一个 Dalvik 虚拟机是由 Zygote 进程创建的,而应用程序进程是由 Zygote 进程 fork 出来的。Zygote 进程是在系统启动时创建的,它会完成虚拟机的初始化、库的加载、预置类库的加载和初始化等操作。

在系统需要一个新的虚拟机实例时,Zygote 通过复制自身最快速的提供一个进程。另外对于一些只读的系统库,所有的虚拟机实例都与 Zygote 共享一块内存区域,大大减少了内存开销。如下图所示

当启动一个应用时,Android 的操作系统需要为应用程序创建新的进程,而这一操作是通过一种写实拷贝技术直接复制 Zygote 进程而来。这就意味着在开始的时候,应用程序进程和 Zygote 进程共享了同一个用来分配内存的堆。然而,当 Zygote 进程或者应用程序进程对该堆进行写操作时,内存就会进行真正的拷贝操作,使得 Zygote 进程和应用程序进程分别拥有自己的一份拷贝。拷贝是一件费时费力的事情,因此为了尽量避免拷贝,Dalvik 虚拟机将自己的堆划分为了两部分

Zygote 进程在启动过程中创建 Dalvik 虚拟机时,只有一个堆。但是当 Zygote 进程在 fork 第一个应用程序进程之前,会将已经使用的那部分堆内存划分为一部分,把还没有使用的堆内存划分为另外一部分。前者就称为 Zygote 堆,后者就称为 Active 堆。无论是 Zygote 进程,还是应用程序进程,当它们需要分配对象的时候,都在 Active 堆上进行。这样就可以使得 Zygote 堆尽可能少的被执行写操作,可以减少执行写时拷贝的操作时间。

Dalvik 虚拟机堆

在 Dalvik 虚拟机中,堆实际上就是一块匿名共享内存。Dalvik 虚拟机并不是直接管理这块匿名共享内存,而是将它封装成一个 mspace,交给 C 库来管理。

为什么这么做呢?

因为内存碎片问题是一个通用问题,不只是 Dalvik 虚拟机、 Java 堆为对象分配内存时会遇到,在 C 库的 malloc 函数在分配内存时也会遇到。

Android 系统使用的 C 库 bionic,使用了 Doug Lea 写的 dlmalloc 内存分配器。调用函数 malloc 的时候,使用的是 dlmalloc 内存分配来分配内存。这是一个成熟的内存分配器,可以很好的解决内存碎片问题。因此,Dalvik 虚拟机就很机制的利用 C 库里面的dlmalloc 分配器来解决内存碎片问题。

这篇关于Android---DVM以及ART对JVM进行优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/217204

相关文章

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain