Android runtime层是如何通过缩减代码来缩减内存的

文章目录

  • 前言:Android 在设备上改进内存的秘密
  • 优化编译器101
  • 代码大小改进
  • 消除写入障碍
  • 隐式暂停检查
  • 合并回调
  • 其他优化改进
  • 代码下沉
  • 循环优化
  • 消除死代码 – SimplifyAlwaysThrows
  • 加载存储消除 – 使用 try catch 块
  • 加载存储消除 – 使用释放/获取操作
  • 新的内联启发式
  • 不断折叠
  • 把它们放在一起
  • 进一步阅读

前言:Android 在设备上改进内存的秘密

请添加图片描述
Android 运行时 (ART) 执行由 Java 或 Kotlin 语言编写的应用程序和系统服务生成的Dalvik字节码。我们不断改进 ART 以生成更小、性能更高的代码。改进 ART 可以使系统和用户体验整体上更好,因为它是 Android 应用程序的共同点。在这篇博文中,我们将讨论在不影响性能的情况下减少代码大小的优化

代码大小是我们关注的关键指标之一,因为生成的文件越小,对内存(RAM 和存储)越有利。通过新版本的 ART,我们估计每台设备可为用户节省约 50-100MB 的空间。这可能正是您更新您喜爱的应用程序或下载新应用程序所需要的。由于 ART 可从 Android 12 开始更新,这些优化适用于1B+ 设备,谷歌在全球范围内为这些设备节省了47-95 PB(47-95 百万 GB!)

本博文中提到的所有改进都是开源的。它们可以通过 ART 主线更新获得,因此您甚至不需要完整的操作系统更新即可获得好处。我们可以把蛋糕倒过来吃!

优化编译器101

ART使用设备上的 dex2oat 工具将应用程序从DEX 格式编译为本机代码。第一步是解析 DEX 代码并生成中间表示(IR)。使用 IR,dex2oat 执行许多代码优化。管道的最后一步是代码生成阶段,其中 dex2oat 将 IR 转换为本机代码(例如,AArch64 汇编)。

优化管道具有执行的阶段,以便每个阶段专注于一组特定的优化。例如,常量折叠是一种优化,尝试用常量值替换指令,例如将加法运算2 + 3折叠为5。
请添加图片描述

IR 可以打印和可视化,但与 Kotlin 语言代码相比非常冗长。出于本博文的目的,我们将展示使用 Kotlin 语言代码进行的优化,但要知道它们正在发生在 IR 代码上。

代码大小改进

对于所有代码大小优化,我们对 Google Play 商店中超过 50 万个 APK 进行了测试,并汇总了结果。

消除写入障碍

我们有一个新的优化过程,称为“写入障碍消除”。写屏障会跟踪自垃圾收集器 (GC) 上次检查以来已修改的对象,以便 GC 可以重新访问它们。例如,如果我们有:
请添加图片描述
以前,我们会为每个对象修改发出一个写屏障,但我们只需要一个写屏障,因为:1)标记将在o本身中设置(而不是在内部对象中),2)垃圾收集不能与这些集合之间的线程进行了交互。

如果指令可能触发 GC(例如 Invokes 和 SuspendChecks),我们将无法消除写入障碍。在下面的示例中,我们不能保证 GC 不需要检查或修改修改之间的跟踪信息:
请添加图片描述
实施这一新通道有助于减少 0.8% 的代码大小

隐式暂停检查

假设我们有几个线程正在运行。挂起检查是安全点(由下图中的房屋表示),我们可以在其中暂停线程执行。使用安全点的原因有很多,其中最重要的是垃圾收集。当发出安全点调用时,线程必须进入安全点并被阻塞,直到它们被释放。

以前的实现是显式布尔检查。我们将加载该值,对其进行测试,并在需要时分支到安全点。
请添加图片描述
隐式挂起检查是一种优化,消除了对测试和分支指令的需要。相反,我们只有一个负载:如果线程需要挂起,该负载将捕获,并且信号处理程序会将代码重定向到挂起检查处理程序,就像该方法进行了调用一样。

请添加图片描述
更详细地说,保留寄存器 rX 预加载了线程内的一个地址,其中我们有一个指向其自身的指针。只要我们不需要进行挂起检查,我们就保留该自指向指针。当我们需要进行挂起检查时,我们清除指针,一旦它对线程可见,第一个LDR rX, [rX]将加载 null,第二个将出现段错误。

挂起请求本质上是要求线程很快挂起一段时间,因此等待第二次加载的轻微延迟是可以接受的。

此优化将代码大小减少了 1.8%

合并回调

已编译方法通常具有入口框架。如果它们拥有它,这些方法必须在返回时解构它,这也称为退出框架。如果一个方法有多个返回指令,它将生成多个退出帧,每个返回指令一个。

通过将返回指令合并为一条,我们能够拥有一个返回点并能够删除多余的退出帧。这对于具有多个 return 语句的 switch 情况特别有用。
请添加图片描述
合并返回可将代码大小减少 1%。

其他优化改进

我们改进了许多现有的优化过程。在这篇博文中,我们将它们分组在同一部分,但它们彼此独立。以下部分中的所有优化都有助于减少 5.7% 的代码大小。

代码下沉

代码下沉是一种优化过程,它将指令下推到不常见的分支,例如以 throw 结尾的路径。这样做是为了减少可能不会使用的指令上浪费的周期。

我们通过 try catch 改进了图中的代码下沉:现在,只要我们不将代码下沉到与它开始的尝试不同的尝试中(或者如果它不在开始的尝试中,则在任何尝试中),我们现在允许下沉代码和)。
请添加图片描述

在第一个示例中,我们可以接收对象创建,因为它只会在if(flag)路径中使用,而不会在其他路径中使用,并且它位于同一个尝试中。通过此更改,在运行时只有当flag为 true时才会运行。在不涉及太多技术细节的情况下,我们可以关注的是实际的对象创建,但加载Object类仍然保留在if之前。这很难用 Kotlin 代码来展示,因为同一条 Kotlin 行在 ART 编译器级别会变成多条指令。

在第二个示例中,我们不能下沉代码,因为我们将在另一个尝试中移动实例创建(可能会抛出异常)。

Code Sinking主要是一种运行时性能优化,但它可以帮助减轻寄存器压力。通过将指令移近其用途,在某些情况下我们可以使用更少的寄存器。使用更少的寄存器意味着更少的移动指令,这最终有助于减少代码大小。

循环优化

循环优化有助于在编译时消除循环。在下面的示例中, foo中的循环将a乘以10,10次。这与乘以100相同。我们启用循环优化以在带有 try catch 的图中工作。

请添加图片描述
在foo中,我们可以优化循环,因为 try catch 是不相关的。

然而,在bar或baz中,我们不对其进行优化。如果循环中有一个 try,或者整个循环是否在 try 内部,那么了解循环将采用的路径并不是一件容易的事。

消除死代码 – 删除不需要的 try 块

我们通过实施优化来删除不包含抛出指令的 try 块,从而改进了死代码消除阶段。我们还可以删除一些 catch 块,只要没有活动的 try 块指向它。

在下面的示例中,我们将bar内联到foo中。之后,我们知道该师不能投掷。稍后的优化过程可以利用这一点并改进代码。

请添加图片描述
只需从 try catch 中删除死代码就足够了,但更好的是,在某些情况下我们允许进行其他优化。如果您还记得的话,当循环有一个 try 或它位于一个 try 内部时,我们不会进行循环优化。通过消除这种冗余的 try/catch,我们可以循环优化,生成更小、更快的代码。
请添加图片描述

消除死代码 – SimplifyAlwaysThrows

在死代码消除阶段,我们有一个称为SimplifyAlwaysThrows 的优化。如果我们检测到调用总是会抛出异常,我们可以安全地丢弃该方法调用之后的任何代码,因为它永远不会被执行。

我们还更新了SimplifyAlwaysThrows,以便在带有 try catch 的图中工作,只要调用本身不在 try 内部。如果它在 try 内部,我们可能会跳转到 catch 块,并且很难找出将要执行的确切路径。

请添加图片描述

我们还改进了:

  • 通过查看参数来检测调用何时抛出。在左边,我们将把divide(1, 0)标记为总是抛出,即使泛型方法并不总是抛出。
  • SimplifyAlwaysThrows适用于所有调用。以前我们有限制,例如不要对导致if的调用执行此操作,但我们可以删除所有限制。
    请添加图片描述

加载存储消除 – 使用 try catch 块

负载存储消除(LSE) 是一种删除冗余负载和存储的优化过程。

我们改进了这个过程以处理图中的 try catch。在foo中,我们可以看到,如果存储/加载不直接与 try 交互,我们可以正常执行 LSE。在bar中,我们可以看到一个示例,我们要么走正常路径而不抛出异常,在这种情况下我们返回1;或者我们抛出,抓住它并返回2。由于每条路径的值都是已知的,因此我们可以删除冗余负载。
请添加图片描述

加载存储消除 – 使用释放/获取操作

我们改进了加载存储消除过程,以在具有释放/获取操作的图表中工作。这些是易失性加载、存储和监视操作。澄清一下,这意味着我们允许 LSE 在具有这些操作的图中工作,但我们不会删除所述操作。

在示例中,i和j是常规整型,vi是易失性整型。在foo中,我们可以跳过加载值,因为集合和加载之间没有释放/获取操作。在bar中,易失性操作发生在它们之间,因此我们无法消除正常负载。请注意,不使用易失性加载结果并不重要——我们无法消除获取操作。
请添加图片描述
此优化与易失性存储和监视器操作(Kotlin 中的同步块)类似。

新的内联启发式

我们的内联传递具有广泛的启发式。有时我们决定不内联一个方法,因为它太大,或者有时我们决定强制内联一个方法,因为它太小(例如,像对象初始化这样的空方法)。

我们实现了一个新的内联启发式:不要内联调用导致抛出。如果我们知道要抛出异常,我们将跳过内联这些方法,因为抛出本身的成本足够高,以至于内联该代码路径是不值得的。

我们将跳到内联的三个方法系列:

  • 在抛出之前计算并打印调试信息。
  • 内联错误构造函数本身。
  • 最后块在我们的优化编译器中被重复。我们有一个用于正常情况(即尝试不抛出),还有一个用于异常情况。我们这样做是因为在特殊情况下我们必须:捕获、执行finally
    块并重新抛出。特殊情况下的方法现在不会内联,但正常情况下的方法会内联。
    请添加图片描述

不断折叠

常量折叠是一种优化过程,如果可能的话,将操作更改为常量。我们实现了一种优化,可以传播在if防护中使用时已知为常量的变量。图中有更多常量可以让我们稍后执行更多优化。

在foo中,我们知道a在if保护中的值为2。我们可以传播该信息并推断b必须是4。同样,在bar中,我们知道cond在if情况下必须为 true ,在else情况下必须为 false (简化图表)。
请添加图片描述

把它们放在一起

如果我们考虑到本博文中的所有代码大小优化,我们的代码大小将减少 9.3%!

从长远来看,一部普通手机可以有 500MB-1GB 的优化代码(实际数字可以更高或更低,具体取决于您安装了多少应用程序,以及您安装了哪些特定应用程序),因此这些优化可以节省大约 50每台设备 -100MB。由于这些优化适用于 1B+ 设备,我们在全球范围内节省了 47-95 PB!

进一步阅读

如果您对代码更改本身感兴趣,请随时查看。本博文中提到的所有改进都是开源的。如果您想帮助全世界的 Android 用户,请考虑为 Android 开源项目做出贡献!

————————————:Santiago Aboy Solanes - 软件工程师

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/167333.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

递归回溯剪枝-子集

LCR 079. 子集 - 力扣&#xff08;LeetCode&#xff09; 方法一 1. 决策树&#xff1a;对于决策树&#xff0c;思考的角度不同&#xff0c;画出的决策树也会不同&#xff0c;这道题可以从两个角度来画决策树。 2. 考虑全局变量的使用&#xff1a; 使用全局变量 List<List&…

Python 基础【五】--数据类型-序列【2023.11.24】

1.定义 Python 中的序列是一块可存放多个值的连续内存空间&#xff0c;所有值按一定顺序排列&#xff0c;每个值所在位置都有一个编号&#xff0c;称其为索引&#xff0c;我们可以通过索引访问其对应值。 list1 [Google, Runoob, 1997, 2000] list2 [1, 2, 3, 4, 5 ] list3…

马克思主义基本原理课后题答案

吐血整理马原word版课后题答案&#xff0c;大家可以自行修改&#xff0c;持续更新中... 【限于篇幅原因&#xff0c;需要的同学可以点赞收藏后&#x1f44d;&#xff0c;扫码下方的公众号&#xff0c;回复相应关键词&#xff08;马原&#xff09;自行领取⭐~】

react的开发中关于图片的知识

React是一个流行的JavaScript库&#xff0c;用于构建用户界面。在React开发中&#xff0c;图片是一个非常重要的元素&#xff0c;可以用于美化界面和展示内容。本篇博客将详细讲解React中关于图片的知识。 1. React中使用图片 在React中使用图片非常简单&#xff0c;只需要使…

【Web题】狼追兔问题

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

解决Resolving Android Dependencies问题

无论是谷歌的Admob&#xff0c;还是Unity的Level play&#xff0c; 在windows&#xff08;win10, win11&#xff09;下&#xff0c;都出现了resolving android dependencies 报错并且卡住的问题&#xff0c;如图: 主要错误&#xff0c;是找不到这个gradlew.bat文件。 在指定位置…

力扣每日一题-统计和小于目标的下标对数目-2023.11.24

力扣每日一题&#xff1a;统计和小于目标的下标对数目 开篇 今天这道力扣打卡题写得我好狼狈&#xff0c;一开始思路有点问题&#xff0c;后面就是对自己的代码到处缝缝补补&#xff0c;最后蒙混过关。只能分享一下大佬的代码&#xff0c;然后我帮大家分享代码的思路。 题目链…

大模型能否生成搜索引擎的未来?

文&#xff5c;郝 鑫 编&#xff5c;刘雨琦 ChatGPT火爆之前&#xff0c;水面下&#xff0c;也有中国公司也在朝着智能助手的方向努力。夸克便是其中之一。在GPT风靡科技圈后&#xff0c;国内就开始陆续冒出一些大模型厂商。对当时夸克而言&#xff0c;做大模型毋庸置疑&am…

django(千锋教育)

创建一个django项目 官网下载python最新版本 配置到环境变量中 打开intlij编辑器 创建django项目 安装django&#xff1a;pip install django 创建django项目: django-admin startproject django01 创建djangoAPP&#xff1a;python manage.py startapp App 启动&#xff1a…

设置定时自动请求测试_自动定时循环发送http_post请求---postman工作笔记001

其实就是创建接口文件夹的时候,有个monitor collection 用来监听接口执行情况,这里就可以设置 可以看到多久执行一次对吧,这里可以设置每几分钟执行一次,一共执行多少次等等 但是这里要说明一下,如果需要使用monitor功能,必须需要登录, 所以如果这里点击monitor collection…

**QT与目标板联合调试_断点仿真**

原文地址: https://blog.csdn.net/u012851408/article/details/86715626

仙女麻麻看过来~这是不是你们在找的外套?

分享女儿的秋冬穿搭 时尚与美观兼具的毛毛外套 洋气百搭不挑人穿 谁穿对都好看系列 经典宽松版型 不臃肿对身材包容性很强 小编墙裂推荐哦&#xff01;&#xff01;

NFT Insider115:The Sandbox开设元宇宙Diorama快闪店,​YGG Web3 游戏峰会已开幕

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members、BeepCrypto联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周报将从NFT市场数据&#xff0c;艺术新闻类&#xff0c;游戏新闻类&#xff0c;虚拟世界类&#…

RevCol:可逆的柱状神经网络

文章目录 摘要1、简介2、方法2.1、Multi-LeVEl ReVERsible Unit2.2、可逆列架构2.2.1、MACRo设计2.2.2、MicRo 设计 2.3、中间监督 3、实验部分3.1、图像分类3.2、目标检测3.3、语义分割3.4、与SOTA基础模型的系统级比较3.5、更多分析实验3.5.1、可逆列架构的性能提升3.5.2、可…

贵金属交易指南:如何在市场中获利?

贵金属市场一直以来都是投资者追逐利润的热门选择&#xff0c;然而&#xff0c;贵金属市场波动较大&#xff0c;在市场中获利并非易事。想要成功&#xff0c;需要理解市场动态和采取适当的策略。万洲金业将为您提供一些实用的贵金属交易指南&#xff0c;帮助您在市场中获利。 …

Java基于springoot开发的企业招聘求职网站

演示视频&#xff1a; https://www.bilibili.com/video/BV1xw411n7Tu/?share_sourcecopy_web&vd_source11344bb73ef9b33550b8202d07ae139b 技术&#xff1a;springootmysqlvuejsbootstrappoi制作word模板 主要功能&#xff1a;求职者可以注册发布简历&#xff0c;选择简…

案例018:基于微信小程序的实习记录系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

【python入门篇】函数(6)

这一节将详细介绍Python中函数的用法&#xff0c;包括函数的定义、调用、参数、返回值、作用域等。 函数的概述&#xff1a; Python函数是一种封装了特定任务的可重用代码块。通过将程序分解为更小、更具体的任务&#xff0c;函数提供了一种有效的方式来组织和管理代码&#xf…

保姆级连接FusionInsight MRS kerberos Hive

数新网络&#xff0c;让每个人享受数据的价值https://xie.infoq.cn/link?targethttps%3A%2F%2Fwww.datacyber.com%2F 概述 本文将介绍在华为云 FusionInsight MRS&#xff08;Managed Relational Service&#xff09;的Kerberos环境中&#xff0c;如何使用Java和DBeaver实现远…

threejs创建一个旋转的正方体【完整代码】

效果&#xff1a; 中文网three.js docs 1.搭建环境 安装three 首先我们需要新建一个项目 vue/react都可 这里以vue为演示 npm i three 找到一个新的页面 在页面script的地方导入three import * as THREE from "three" 或者自己逐个导入 import {PerspectiveC…