随着业务的发展,百度APP有很多大内存业务场景如直播、短视频、小程序、百度识图等,通过线上页面统计数据得知超过150M页面有40个,耗内存最多的页面有400M。单个页面不会有内存或者稳定性问题,但是当用户浏览了很多页面之后,累加起来内存已经很高了,再加上我们为了追求秒开,经常采用的思路是以空间换取时间,从而导致APP处于一个内存高水位状态,在这种情况下如果打开一个大内存页面,中低端机极大概率会出现OOM类型的崩溃。
内存管控方案应运而生,该方案重点解决的问题是在内存水位很高的情况下,保证APP稳定性又兼顾用户体验,延长APP使用时长同时避免OOM。
该技术方案在百度APP于22年Q1顺利上线,随着基础服务层和越来越多的业务线接入,尤其是OOM频发的页面接入后,在降低OOM率方面发挥了重大作用,效果非常明显。
内存管控整体方案架构图如下所示:
目前iOS系统中存在类似的方案,专业名称为内存报警机制,当设备可用内存下降到到危险状态时,Mach系统的pageout 守护程序会查询进程列表及其驻留页面数,向驻留页面数最高的进程发送NOTE_VM_PRESSURE ,被选中的进程会响应这个压力通知,本质上就是APP收到系统的didReceiveMemoryWarning 内存警告,APP释放部分内存达到降低手机内存负载的目标。有人会问iOS系统提供了内存报警通知,为什么我们还会做貌似类似的事情,这是因为我们对系统的内存报警机制做了如下两点补充:
百度APP实时监控内存采用如下方案:在子线程开启定时器,每隔3S去采样一次内存phys_footprint字段数据,以此作为衡量的内存的唯一指标,其他字段值一律不要获取,因为多增加一个变量会多增加CPU计算量。实践数据表明,第一、单次获取phys_footprint耗时小于1us,每隔3S获取phys_footprint没有引起CPU占比的涨幅,也就是说不会带来性能问题;第二、3S的采样周期实时性完全满足我们工程的要求,正常情况下,开启一个页面到页面可交互需要1.5S+,采样周期如果太长,会存在页面内存已经飙升但是还没来得及做管控,采样周期太短会浪费过多的CPU资源。
为什么我们选用phys_footprint作为内存衡量指标,而不用其他字段,需要重点解释一下。iOS端所有的内存相关指标都集中在task_vm_info结构体中,下载XNU最新开源代码(https://opensource.apple.com/source/xnu/),代码路径:osfmk/mach/task_info.h,具体字段值如下所示:
struct task_vm_info {
mach_vm_size_t virtual_size; /* virtual memory size (bytes) */
integer_t page_size;
mach_vm_size_t resident_size; /* resident memory size (bytes) */
/* 省略 */
mach_vm_size_t phys_footprint;
/* 省略 */
}
iOS开发演变的这几年历程中,受Android端内存指标影响,我们先后使用过各种内存指标,常见的如virtual_size( 虚拟内存)、resident_size(驻留内存)和phys_footprint,那究竟使用哪个指标是合理的?我们知道iOS使用的是低内存清理机制叫Jetsam,这个机制有点类似于Linux的“Out-of-Memory”杀手,当内存压力过大时,Jetsam会把一些优先级不高或者占用内存过大的进程杀掉。就是说内存处于危险状态时Jetsam决定kill哪个进程,因此Jetsam衡量内存水位指标绝对是众多内存指标中最为合理的一项,接下来我们看Jetsam机制源码。
我们再次回到XNU源码中,查看代码bsd/kern/kern_memorystatus.c,重点查看函数 memorystatus_kill_hiwat_proc,这是jetsam核心代码,用于kill高内存分配进程的关键函数,具体实现如下所示:
static boolean_t
memorystatus_kill_hiwat_proc(uint32_t *errors, boolean_t *purged, uint64_t *memory_reclaimed)
{
next_p = memorystatus_get_first_proc_locked(&i, TRUE);
while (next_p) {
/* 省略 */
footprint_in_bytes = get_task_phys_footprint(p->task);
skip = (footprint_in_bytes <= memlimit_in_bytes);
if (skip) {
continue;
} else {
memorystatus_kill_proc(p, kMemorystatusKilledHiwat, jetsam_reason, &killed, &footprint_in_bytes);
/* 省略 */
}
}
首先通过memorystatus_get_first_proc_locked去优先级队列里面取出优先级最低的进程,如果内存超过阈值,将通过memorystatus_kill_proc杀掉这个进程,否则跳过取下一个进程。我们看到Jetsam是通过 get_task_phys_footprint方法获取内存水位来决定是不是需要kill该进程,因此使用phys_footprint作为APP内存指标是最合适的。
关于 phys_footprint 的定义,我们回到 XNU 源码中,查看代码 osfmk/kern/task.c ,有phys_footprint 的注释定义。
* Physical footprint: This is the sum of:
* + (internal - alternate_accounting)
* + (internal_compressed - alternate_accounting_compressed)
* + iokit_mapped
* + purgeable_nonvolatile
* + purgeable_nonvolatile_compressed
* + page_table
phys_footprint = (internal - alternate_accounting) + (internal_compressed - alternate_accounting_compressed) + iokit_mapped + purgeable_nonvolatile + purgeable_nonvolatile_compressed + page_table 。
字段 | 具体含义 |
---|---|
internal | 在iOS中表示的就是resident_size驻留内存 |
internal_compressed | iOS 上没有交换空间机制,取而代之使用Compressed memory,是在内存紧张时能够将最近使用过的内存占用压缩至原有大小的一半以下,并且能够在需要时解压复用 |
iokit_mapped | io设备映射占用的内存,其实是不能使用purgeable memory的部分 |
alternate_accounting | iokit映射占用的dirty页 |
page_table | 虚拟地址映射表内存 |
purgeable_nonvolatile | 下面重点介绍 |
purgeable内存是iOS系统为开发者提供的一层cache机制,分为volatile、empty和non_volatile三种类型,volatile表示该内存资源是暂时不被使用的,系统将在内存吃紧的时候回收掉它,使用这种类型资源前要查询是否已经无效了(变成empty状态);empty表示该内存资源明确不用了需要立即释放;non_volatile表示该内存资源一直有用,不能被回收。volatile和empty状态的资源不计入进程自己的mem footprint,它算系统的cache内存,nonvolatile会算自己进程的内存,被虚拟内存系统回收时不会被换出到磁盘,所以phys_footprint在计算内存时,只计算了nonvolatile类型,对于volatile、empty没做计算。
为了能够更精准的对页面的内存进行分析和预测,我们在实时内存监控的基础上,开发了页面内存预测方案。具体来说,在前面通过定时器我们知道了每隔3S手机APP内存状态,本方案通过经验数据直接预测未来一段时间内存的涨幅,让业务线可以更加从容的释放内存。我们知道当新打开一个页面时存在内存飙升的情景,这个时候3S的采样周期未到,内存已经上涨很多,内存管控方案还未生效APP极有可能已经OOM了。我们的方案是通过页面内存计算,在打开新页面前一刻, 就知道接下来页面内存可能会涨到多少,如果进入危险水位,实时释放内存以降低OOM率,通过这种精细化处理进一步提前降低内存峰值。页面内存计算方案如下所示,首先,当前页面是P1页面,当有页面跳转发生,将要通过push操作进入到P2页面时,记录当前百度APP内存phys_footprint值为M1,当从P2页面同样发生跳转到其他页面时,记录百度APP内存phys_footprint值为M2,那么M2-M1为P2页面内存。
注意,我们只通过push方式统计了页面内存,没有通过pop方式统计,有两个原因,第一、通过线上数据发现,pop方式时因页面已经打开,并且会创建单例导致内存统计存在很多badcase,push方式时页面从未创建也不会有单例,数据相对准确;第二、通过push方式已经可以覆盖所有页面了,pop方式不需要统计。
关于内存水位的制定直接决定了本方案实际收益的大小,水位阈值制定太小会导致频繁的内存管控影响业务效果,水位阈值制定的太大,与实际的Jetsam水位线偏离过大,导致内存管控无法生效,可能会出现APP已经OOM了,管控方案还没生效,水位线的制定非常关键。关于危险水位线的制定,必须结合Jetsam原理,目前苹果官方没有公开Jetsam水位的文档,业界有如下方法解决方案。
具体来说从手机"设置->隐私->分析与改进->分析数据"这条操作路径中,可以拿到JetsamEvent 开头的日志。这些日志中就可以获取一些关于 App 的内存信息,查找崩溃原因时需要关注 per-process-limit 部分的 rpages,其中rpages代表进程占用的内存页数量。pageSize代表当前设备物理内存页的大小,在 JetsamEvent 开头的系统日志里可以找到 pageSize 的值,那么pageSize * rpage的值代表目前该进程OOM时使用的内存大小,可作为进程可用内存的上限。
实际操作过程中,发现此方法可操作性不强,因为同一台手机不同的JetsamEvent日志rpages值变化太大,用iphone12的测试结果显示,从400到800都有,pageSize是固定值16384Byte,按照最高值计算当前 App 的内存限制值:pageSize * rpages / 1024 /1024 =16384 * 800 / 1024 / 1024 = 12.5M,按这个结果iphone12最大的内存阈值是12.5M,置信度明显有问题。
首先必须越狱手机获取root权限,通过XNU源码中的数据结构、宏定义和函数获取OOM阈值,参考XNU最新开源代码(https://opensource.apple.com/source/xnu/),代码路径:bsd/sys/kern_memorystatus.h,关键数据结构memorystatus_priority_entry,定义如下,其中pid代表进程标识,priority代表JetSam中的优先级,limit就是我们要找的水位线上线。同时,在文件kern_memorystatus.h有如下跟进程优先级相关的宏命令,其中通过MEMORYSTATUS_CMD_GET_PRIORITY_LIST宏定义可以获取进程的优先级列表以及每个进程的内存水位线。
typedef struct memorystatus_priority_entry {
pid_t pid;
int32_t priority;
uint64_t user_data;
int32_t limit;
uint32_t state;
} memorystatus_priority_entry_t;
#define MEMORYSTATUS_CMD_GET_PRIORITY_LIST 1
#define MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES 2
#define MEMORYSTATUS_CMD_GET_JETSAM_SNAPSHOT 3
#define MEMORYSTATUS_CMD_GET_PRESSURE_STATUS 4
/* 省略 */
最后通过调用系统函数memorystatus_control的实现可获取memorystatus_priority_entry结构体值,其中limit字段代表水位线, 代码路径:bsd/kern/kern_memorystatus.c
int memorystatus_control(struct proc *p __unused, struct memorystatus_control_args *args, int *ret) {
/* 省略 */
switch (args->command) {
case MEMORYSTATUS_CMD_GET_PRIORITY_LIST:
error = memorystatus_cmd_get_priority_list(args->buffer, args->buffersize, ret);
break;
/* 省略 */
}
实践证明这种方法是可行的,唯一缺点是需要获取root权限,我们要获取不同机型的内存阈值,需要将这些设备全部越狱。
百度APP采用的方案是综合百度APP自身的线上业务数据,采用主动触发OOM获取内存阈值方案,结合多方数据最后确定内存危险水位阈值。
通过线上内存采样打点,获取了百度APP不同机型在使用过程中的内存值,然后经过服务端数据聚合,我们明确知道了百度APP在没有发生OOM情况下不同机型的内存最大值,这份线上数据很重要,虽然不是内存阈值的,但是内存阈值肯定是高于该值的。
技术方案在第五节做过详细介绍,这儿不再赘述,通过服务端对页面内存数据挖掘后,我们明确知道了不同机型新开一个页面时最大的内存涨幅。
开启定时器任务每隔1S分配20M内存,示例代码如下所示:
int size = 20 * 1024 * 1024;
char *info = malloc(size);
memset(info, 1, size);
同时监控内存变化,在控制台输出,随着可用内存越来越少,触发Jetsam机制,直到发生OOM,从而得到OOM前内存阈值。
(int64_t)memoryUsage {
int64_t memoryUsageInByte = 0;
struct task_vm_info info;
mach_msg_type_number_t size = TASK_VM_INFO_COUNT;
kern_return_t kerr = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &info, &size);
if (kerr == KERN_SUCCESS ) {
memoryUsageInByte = info.phys_footprint;
}
return memoryUsageInByte;
}
经过前面三个步骤,我们获取了不同机型的三个阈值,分别是内存数据摸底阈值、页面内存阈值、主动触发OOM获取的阈值,为了让业务更从容地释放内存, 内存管控阈值为主动触发OOM获取的阈值减去页面内存阈值,如果该值小于内存数据摸底阈值,那么内存数据摸底阈值就是该机型内存管控阈值。百度APP采用的这个技术方案不需要越狱手机,通过主动触发OOM获取的阈值体现了Jetsam机制,更具有可操作性;同时结合自身线上数据,针对手百场景定制化挖掘。
最后,总结百度APP内存管控方案具有如下特点:
该方案上线后,随着Q2基础服务层和业务线接入,实现OOM降低一半的收益,并且业务层接入成本很低,后续会推动更多内存大户和OOM频发的页面接入。感谢各位阅读至此,如有问题请不吝指正。
[1] 、OOM探究:XNU 内存状态管理:https://www.jianshu.com/p/4458700a8ba8
[2]、XNU源码:https://opensource.apple.com/source/xnu/
[3]、《深入解析Mac OS X & iOS操作系统》
[4]、iOS Out-Of-Memory 原理阐述及方案调研:https://juejin.cn/post/6844903749836603400#heading-7
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/dETOGD3NYU2SdZhxGu0SZg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。