Android 中 app freezer 原理详解(一):R 版本

基于版本:Android R

0. 前言

在之前的两篇博文《Android 中app内存回收优化(一)》 《Android 中app内存回收优化(二)》中详细剖析了 Android 中 app 内存优化的流程。这个机制的管理通过 CachedAppOptimizer 类管理,为什么叫这个名字,而不叫 AppCompact 等?在之前的两篇博文中也提到了,因为该类中还管理了一个重要功能:freezer,一个针对应用进程长期处于 Cached 状态的优化。

本文将继续分析 CachedAppOptimizer 类另一个功能 freezer。

1. Freezer 触发

《Android oom_adj 更新原理(二)》中详细剖析了 OomAdjuster.applyOomAdjLocked() 函数,在 oom_adj 发生变化之后会重新 compute 然后在 apply, 在该函数中就是通过调用 updateAppFreezeStateLocked(app) 来确认是否冻结进程。

1.1 updateAppFreezeStateLocked()

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javavoid updateAppFreezeStateLocked(ProcessRecord app) {// 确定该功能是使能的if (!mCachedAppOptimizer.useFreezer()) {return;}// 如果该进程处于 frozen状态,但sholudNoFreeze变为true,需要解冻if (app.frozen && app.shouldNotFreeze) {mCachedAppOptimizer.unfreezeAppLocked(app);}// 如果该进程的 adj处于 CACHED,并且可以冻结,则调用 freezeAppAsync() 冻结// 如果该进程的 adj离开 CACHED,则解冻if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && !app.frozen && !app.shouldNotFreeze) {mCachedAppOptimizer.freezeAppAsync(app);} else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ && app.frozen) {mCachedAppOptimizer.unfreezeAppLocked(app);}}

2. CachedAppOptimizer.init()

对于CachedAppOptimizer 的构造调用,以及 init() 函数的触发流程,可以参考《Android 中app内存回收优化(一)》 一文第 1 节 和 第 2 节。

frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.javapublic void init() {...synchronized (mPhenotypeFlagLock) {...updateUseFreezer();}}

2.1 updateUseFreezer()

    private void updateUseFreezer() {// 获取settings 中属性 cached_apps_freezer 的值,根据属性值初始化变量mUseFreezerfinal String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(),Settings.Global.CACHED_APPS_FREEZER_ENABLED);if ("disabled".equals(configOverride)) {mUseFreezer = false;} else if ("enabled".equals(configOverride)|| DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {mUseFreezer = isFreezerSupported();}if (mUseFreezer && mFreezeHandler == null) {Slog.d(TAG_AM, "Freezer enabled");enableFreezer(true);if (!mCachedAppOptimizerThread.isAlive()) {mCachedAppOptimizerThread.start();}mFreezeHandler = new FreezeHandler();Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),Process.THREAD_GROUP_SYSTEM);} else {enableFreezer(false);}}

首先确认 freezer 功能是否使能,用个流程图来说明比较清晰:

当 freezer 使能,就会:

  • 调用 enableFreezer() 进行使能,详细的流程可以查看第 4 节;
  • 如果 CachedAppOptimizer 中的 ServiceThread 没有启动,则启动;
  • 创建 free handler,用以处理 freezer 相关消息;
  • 设置 ServiceThread 的优先级为 THREAD_GROUP_SYSTEM;

3. cgroups 简介

cgroups (全称:control groups) 是 Linux 内核提供的一种可以限制单个进程或者多个进程所使用资源的机制,可以对 CPU、memory 等资源实现精细化的控制。目前越来越活的轻量级容器 Docker 就使用了 cgroups 提供的资源限制能力来完成 CPU、memory 等部门的资源控制。

cgroups 为每种可以控制的资源定义了一个子系统,典型的子系统如下:

  • cpu:主要限制进程的 cpu 使用率;
  • cpuaat:可以统计 cgroups 中的进程的 cpu 使用报告;
  • cpuset:可以为 cgroups 中的进程分配单独的 cpu 节点或内存节点;
  • memory:可以限制进程的 memory 使用量;
  • blkio:可以限制进程的块设备 io;
  • devices:可以控制进程能够访问某些设备;
  • freezer:可以挂起或恢复 cgroups 中的进程;
  • net_cls:可以标记 cgroups 中进程的网络数据包,然后可以使用 tc (traffic control)模块对数据包进行控制;
  • ns:可以使不同的 cgroups 下面的进程使用不同的 namespace;

Android Q 或更高版本通过 task profiles 使用 cgroup 抽象层,task profiles 可以用来描述应用于某个线程或进程的一个set 或 sets 的限制。系统依照 task profiles 的规定选择一个或多个适当的 cgroups。通过这种限制,可以对底层的 cgroup 功能集进行更改,而不会影响较高的软件层。

具体的细节可以查看博文:《Android 中 cgroup抽象层详解》

本文的很多重要特性都是通过 profile 的方式完成。

4. enableFreezer()

主要是调用 enableFreezerInternal() 函数:

    private static native void enableFreezerInternal(boolean enable);
frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cppstatic void com_android_server_am_CachedAppOptimizer_enableFreezerInternal(JNIEnv *env, jobject clazz, jboolean enable) {bool success = true;if (enable) {success = SetTaskProfiles(0, {"FreezerEnabled"}, true);} else {success = SetTaskProfiles(0, {"FreezerDisabled"}, true);}if (!success) {jniThrowException(env, "java/lang/RuntimeException", "Unknown error");}
}

 通过接口 SetTaskProfiles() 往对应的节点写入特定的 value 值,详细可以查看博文:《Android 中 cgroup抽象层详解》

5. freezeAppAsync()

参数为 ProcessRecord 类型,也就是对应的进程。

    void freezeAppAsync(ProcessRecord app) {mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);mFreezeHandler.sendMessageDelayed(mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),FREEZE_TIMEOUT_MS);}

注意,

  • 如果该进程已经发送 freeze 请求,再次发送请求时,先取消原来的消息;
  • 发送消息 SET_FROZEN_PROCESS_MSG,请求 freeze;
  • 消息处理的延时时长为 FREEZE_TIMEOUT_MS(10 min),如果10 分钟之后,冻结该进程的消息还没有被取消,则进入冻结进程的流程;

6. unfreezeAppLocked()

    void unfreezeAppLocked(ProcessRecord app) {// 首先,取消之前该进程的冻结请求mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);// 如果进程还没有冻结,则无需做解冻处理if (!app.frozen) {return;}/********进程处于冻结,进行解冻处理*********/boolean processKilled = false;// 冻住的进程可以接收异步binder请求,但是不会处理,只是放入binder buffer, 过多的请求会导致buffer耗尽;// 这里需要确认下该进程在解冻之前,进程是否在冰冻期间收到同步的binder 请求,有则kill该进程try {int freezeInfo = getBinderFreezeInfo(app.pid);if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "+ " received sync transactions while frozen, killing");app.kill("Sync transaction while in frozen state",ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_INVALID_STATE, true);processKilled = true;}if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) {Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "+ " received async transactions while frozen");}} catch (Exception e) {Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + app.pid + " "+ app.processName + ". Killing it. Exception: " + e);app.kill("Unable to query binder frozen stats",ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_INVALID_STATE, true);processKilled = true;}if (processKilled) {return;}long freezeTime = app.freezeUnfreezeTime;try {freezeBinder(app.pid, false);} catch (RuntimeException e) {Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName+ ". Killing it");app.kill("Unable to unfreeze",ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_INVALID_STATE, true);return;}try {Process.setProcessFrozen(app.pid, app.uid, false);app.freezeUnfreezeTime = SystemClock.uptimeMillis();app.frozen = false;} catch (Exception e) {Slog.e(TAG_AM, "Unable to unfreeze " + app.pid + " " + app.processName+ ". This might cause inconsistency or UI hangs.");}if (!app.frozen) {if (DEBUG_FREEZER) {Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName);}mFreezeHandler.sendMessage(mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG,app.pid,(int) Math.min(app.freezeUnfreezeTime - freezeTime, Integer.MAX_VALUE),app.processName));}}

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

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

相关文章

【Linux | Shell】结构化命令2 - test命令、方括号测试条件、case命令

目录 一、概述二、test 命令2.1 test 命令2.2 方括号测试条件2.3 test 命令和测试条件可以判断的 3 类条件2.3.1 数值比较2.3.2 字符串比较 三、复合条件测试四、if-then 的高级特性五、case 命令 一、概述 上篇文章介绍了 if 语句相关知识。但 if 语句只能执行命令&#xff0c…

Docker 的数据管理、容器互联、镜像创建

目录 一、数据管理 1.数据卷 2. 数据卷容器 二、容器互联&#xff08;使用centos镜像&#xff09; 三、Docker 镜像的创建 1.基于现有镜像创建 1.1首先启动一个镜像&#xff0c;在容器里修改 1.2将修改后的容器提交为新的镜像&#xff0c;需使用该容器的id号创建新镜像 …

JAVA SE -- 第十天

&#xff08;全部来自“韩顺平教育”&#xff09; 一、枚举&#xff08;enumeration&#xff0c;简写enum&#xff09; 枚举是一组常量的集合 1、实现方式 a.自定义类实现枚举 b.使用enum关键字实现枚举 二、自定义类实现枚举 1、注意事项 ①不需要提供setXxx方法&#xff…

HTTP、HTTPS协议详解

文章目录 HTTP是什么报文结构请求头部响应头部 工作原理用户点击一个URL链接后&#xff0c;浏览器和web服务器会执行什么http的版本持久连接和非持久连接无状态与有状态Cookie和Sessionhttp方法&#xff1a;get和post的区别 状态码 HTTPS是什么ssl如何搞到证书nginx中的部署 加…

【从删库到跑路】MySQL数据库的索引(一)——索引的结构(BTree B+Tree Hash),语法等

&#x1f38a;专栏【MySQL】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 &#x1f970;欢迎并且感谢大家指出小吉的问题 文章目录 &#x1f354;概述&#x1f354;索引结构⭐B-Tree多路平衡查找树&#x1f3f3;️‍&a…

【iOS】weak关键字的实现原理

前言 关于什么是weak关键字可以去看看我以前的一篇博客&#xff1a;【OC】 属性关键字 weak原理 1. SideTable SideTable 这个结构体&#xff0c;前辈给它总结了一个很形象的名字叫引用计数和弱引用依赖表&#xff0c;因为它主要用于管理对象的引用计数和 weak 表。在 NSOb…

Vite + Vue3 + Ts 【免key、免账号实战本地运行GPT】

&#x1f414; 前期回顾 Vue3 Ts Vite —— 封装庆祝彩屑纷飞 示例_彩色之外的博客-CSDN博客封装 彩屑纷飞 示例https://blog.csdn.net/m0_57904695/article/details/131718019?spm1001.2014.3001.5501 目录 &#x1f30d; 公网 &#x1f6f9; 本地 &#x1fa82; 源码 &…

【前端|CSS系列第4篇】CSS布局之网格布局

前言 最近在做的一个项目前台首页有一个展示词条的功能&#xff0c;每一个词条都以一个固定大小的词条卡片进行展示&#xff0c;要将所有的词条卡片展示出来&#xff0c;大概是下面这种布局 每一行的卡片数目会随着屏幕大小自动变化&#xff0c;并且希望整个卡片区域周围不要…

20230721 Essex UK, Dongbing Gu 公开讲座--机器人前沿

个人主页&#xff1a; https://www.essex.ac.uk/people/GUDON81301/dongbing-gu 机器人领域任务的特点&#xff1a;dull, dirty, dangerous tasks in remote spaces 机器鱼&#xff1a; 实时港口环境监测 机器鱼群探索算法 化学传感器 水面声呐定位系统/SLAM/通信问题 Robotic …

C—数据的储存(下)

文章目录 前言&#x1f31f;一、练习一下&#x1f30f;1.例一&#x1f30f;2.例二&#x1f30f;3.例三&#x1f30f;4.例四 &#x1f31f;二、浮点型在内存中的储存&#x1f30f;1.浮点数&#x1f30f;2.浮点数存储&#x1f4ab;&#xff08;1&#xff09;.二进制浮点数&#x…

QDialog的两种显示方式

QDialog的两种显示方式 模态显示非模态显示 QDialog不能嵌入到其他窗口中显示&#xff08;无论继承与否&#xff09; 模态显示 d->exec(); 阻塞程序的执行 非模态显示 d->show(); 不阻塞程序

OpenCV4图像处理-图像交互式分割-GrabCut

本文将实现一个与人&#xff08;鼠标&#xff09;交互从而分割背景的程序。 GrabCut 1.理论介绍2. 鼠标交互3. GrabCut 1.理论介绍 用户指定前景的大体区域&#xff0c;剩下为背景区域&#xff0c;还可以明确指出某些地方为前景或者背景&#xff0c;GrabCut算法采用分段迭代的…

MySQL主从复制与读写分离

文章目录 一.前言二.主从复制原理1.MySQL的复制类型2.MySQL主从复制的工作过程2.1 MysQL主从复制延迟原因2.2问题解决方法2.3 MySQL 有几种同步方式2.3.1 异步复制2.3.2 同步复制2.3.3 半同步复制2.3.4 增强半同步复制&#xff08;lossless Semi-Sync Replication、无损复制&am…

消息队列 CKafka 跨洋数据同步性能优化

导语 本文主要介绍了 CKafka 在跨洋场景中遇到的一个地域间数据同步延时大的问题&#xff0c;跨地域延时问题比较典型&#xff0c;所以详细记录下来做个总结。 一. 背景 为了满足客户跨地域容灾、冷备的诉求&#xff0c;消息队列 CKafka 通过连接器功能&#xff0c;提供了跨…

进程控制学习笔记

文章目录 进程退出孤儿进程僵尸进程进程回收waitpid()函数 进程退出 子进程的退出需要父进程对其资源的释放&#xff0c;子进程只能对用户区的数据进行释放&#xff0c;无法完成对内核区的释放。 可以获取到。 两个退出的区别&#xff1a; 系统调用不会刷新缓冲区&#xff…

Kubernetes对象深入学习之四:对象属性编码实战

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 本文是《Kubernetes对象深入学习》系列的第四篇&#xff0c;前面咱们读源码和文档&#xff0c;从理论上学习了kubernetes的对象相关的知识&#xff…

python识别极验4滑块验证码实战

闲得无聊&#xff0c;趁着休息研究了一下极验4滑块验证码的安全性&#xff0c;是否有机器识别、自动化拖拽的可能性。首先看一下效果 如何识别验证码 1、下载图片 下载图片可以参考博客《采集极验4滑块验证码图片数据》 2、标记图片 3、标记滑动距离 实现代码 __author__ &…

【C++】C++11

文章目录 C111. 统一的列表初始化1.1 {}初始化 2. 声明2.1 auto2.2 decltype2.3 nullptr 3. 右值引用和移动语义3.1 左值引用和右值引用3.2 左值引用与右值引用比较3.3 右值引用使用的场景和意义3.4 完美转发 4. 可变参数模板5. lambda表达式5.1 函数对象与lambda表达式 6. 线程…

STM32入门之创建工程模板

1.STM32固件库的结构图如下。从图中可以看出&#xff0c;我们在配置STM32的固件库时需要配置用户层、CMSIS层的文件。配置库文件即正确的配置这些函数的文件。CMSIS(Cortex Microcontroller Software Interface Standard)是ARM公司提供的微控制器软件接口标准&#xff0c;所有使…

Data Structure, Algorithm,and Applications in C++

在学习这本书进阶内容之前&#xff0c;我们可以跟着它的第一章部分再巩固和复习。本书由Sartaj Sahni撰写&#xff0c;由王立柱和刘志红翻译。全书通俗易懂&#xff0c;内容丰富&#xff0c;是巩固C内容的不二选择。希望本文对各位有所帮助。 目录 1.函数与参数 1.1.传值参数…