让人懵逼的 iOS 系统内存分配问题

最近应用中出现低内存被杀的情况,所以就想办法对造成这一个问题的根源进行定位,主要是两个比较 Low 的思路

  • 线下使用 InstrumentsMLeaksFinder 来进行内存泄露的定位,后期可能考虑把测试阶段的内存泄漏对象和泄漏位置上报到测试服务器进行数据分析,进而更精确定位问题
  • 线上继续统计 Abort 率,然后对于出现内存警告、Abort 的情况,及时上报 App 的内存走势,这个走势包括 App 使用的物理内存大小,以及当前设备的整体内存使用情况。当然不会持续收集内存,我们会在 ViewController 初始化和销毁的时候进行收集,这样基本可以把问题定位到 Controller 级别。

让人懵逼的地方就是如何获取 app 的内存和设备的整体内存情况,在网上查了一下,答案千奇百怪,而且统计的结果差异也比较大,所以才有了此文

系统内存分配

据查阅 Apple 的官方文档,操作系统的内存主要分为 Used MemoryFree MemoryUsed Memory 又可以分为Wired MemoryActive MemoryInactive Memory,同时提到了一个Purgeable Memory ,暂且把它归类为 Active Memory 吧。

  • Free Memory:未使用的 RAM 容量,随时可以被应用分配使用

  • Wired Memory:用来存放内核代码和数据结构,它主要为内核服务,如负责网络、文件系统之类的;对于应用、framework、一些用户级别的软件是没办法分配此内存的。但是应用程序也会对 Wired Memory 的分配有所影响。

  • Active Memory:活跃的内存,正在被使用或很短时间内被使用过

  • Inactive Memory:最近被使用过,但是目前处于不活跃状态

    例如,如果您使用了邮件然后退出,则邮件曾经使用的 RAM 会标记为“不活跃”内存。“不活跃”内存可供其他应用软件使用,就像“可用”内存一样。但是,如果在其他应用软件占用邮件的“不活跃”内存之前打开了邮件,邮件的打开速度会更快,因为其“不活跃”内存会转换为“活跃”内存,而不是从较慢的驱动器进行载入。

  • Purgeable Memory:这个是查阅资料发现的,同时第三方库中有统计此内存的大小,所以记录一下。可以理解为可释放的内存,主要是大对象或大内存块才可以使用的内存,此内存会在内存紧张的时候自动释放掉,一会可以查看 Demo 来验证这一事实。

应用物理内存

这个就是理解上的物理内存,对于 app 的内存使用应该检测这一数值的变化,而检测虚拟内存的话意义不大,对于物理内存和虚拟内存在 iOS 上的分配可以查看这篇文章:先弄清楚这里的学问,再来谈 iOS 内存管理与优化(一)。对于物理内存的用量检测方法比较常见:(更新正确的获取App内存占用的方法:http://ddrccw.github.io/2017/12/30/reverse-xcode-with-lldb-and-hopper-disassemblerhttp://www.samirchen.com/ios-app-memory-usage/

// 获得当前 App 的内存占用情况
- (NSUInteger)getResidentMemory {
    struct task_basic_info t_info;
    mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
    
    int r = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
    if (r == KERN_SUCCESS) {
        
        NSLog(@"resident_size %lu",t_info.resident_size / 1024/1024);
        
        return t_info.resident_size;
    }
    else {
        return -1;
    }
}

测试

我主要是在 app 中放了一个按钮,每点击一次分配一百兆物理内存,然后查看 app 的内存使用情况以及系统的内存分配情况,对于系统的内存分配查看方式,可以参考这里:iOS-System-Services/System Services/Utilities/SSMemoryInfo.m

在 app 低内存崩溃前,一直收集点击按钮后的内存变化,如下

2017-03-09 21:28:03.112138 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:03.112280 LDAPM[491:46357] free 549.734375
2017-03-09 21:28:03.112338 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:03.112361 LDAPM[491:46357] used 1226.390625
2017-03-09 21:28:03.112381 LDAPM[491:46357] active 740.156250
2017-03-09 21:28:03.112399 LDAPM[491:46357] inactive 291.109375
2017-03-09 21:28:03.112417 LDAPM[491:46357] wired 195.125000
2017-03-09 21:28:03.112544 LDAPM[491:46357] purgableMemory 11.515625
2017-03-09 21:28:03.112613 LDAPM[491:46357] resident_size 27

2017-03-09 21:28:36.326194 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:36.326284 LDAPM[491:46357] free 448.093750
2017-03-09 21:28:36.326307 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:36.326322 LDAPM[491:46357] used 1330.734375
2017-03-09 21:28:36.326340 LDAPM[491:46357] active 846.015625
2017-03-09 21:28:36.326358 LDAPM[491:46357] inactive 289.593750
2017-03-09 21:28:36.326377 LDAPM[491:46357] wired 195.125000
2017-03-09 21:28:36.326395 LDAPM[491:46357] purgableMemory 12.531250
2017-03-09 21:28:36.326450 LDAPM[491:46357] resident_size 134

2017-03-09 21:28:42.708484 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:42.708571 LDAPM[491:46357] free 352.718750
2017-03-09 21:28:42.708593 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:42.708607 LDAPM[491:46357] used 1427.968750
2017-03-09 21:28:42.708626 LDAPM[491:46357] active 944.703125
2017-03-09 21:28:42.708656 LDAPM[491:46357] inactive 289.203125
2017-03-09 21:28:42.708677 LDAPM[491:46357] wired 194.062500
2017-03-09 21:28:42.708695 LDAPM[491:46357] purgableMemory 12.531250
2017-03-09 21:28:42.708715 LDAPM[491:46357] resident_size 234

2017-03-09 21:28:46.940049 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:46.940146 LDAPM[491:46357] free 252.437500
2017-03-09 21:28:46.940166 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:46.940185 LDAPM[491:46357] used 1527.406250
2017-03-09 21:28:46.940203 LDAPM[491:46357] active 1043.875000
2017-03-09 21:28:46.940221 LDAPM[491:46357] inactive 289.484375
2017-03-09 21:28:46.940239 LDAPM[491:46357] wired 194.046875
2017-03-09 21:28:46.940256 LDAPM[491:46357] purgableMemory 12.531250
2017-03-09 21:28:46.940275 LDAPM[491:46357] resident_size 334

2017-03-09 21:28:49.930067 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:49.930154 LDAPM[491:46357] free 152.187500
2017-03-09 21:28:49.930177 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:49.930221 LDAPM[491:46357] used 1627.515625
2017-03-09 21:28:49.930248 LDAPM[491:46357] active 1143.921875
2017-03-09 21:28:49.930267 LDAPM[491:46357] inactive 289.515625
2017-03-09 21:28:49.930285 LDAPM[491:46357] wired 194.078125
2017-03-09 21:28:49.930303 LDAPM[491:46357] purgableMemory 12.593750
2017-03-09 21:28:49.930322 LDAPM[491:46357] resident_size 434

2017-03-09 21:28:52.780752 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:52.780830 LDAPM[491:46357] free 82.390625
2017-03-09 21:28:52.780852 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:52.780867 LDAPM[491:46357] used 1748.640625
2017-03-09 21:28:52.780918 LDAPM[491:46357] active 1078.281250
2017-03-09 21:28:52.780943 LDAPM[491:46357] inactive 479.546875
2017-03-09 21:28:52.780961 LDAPM[491:46357] wired 190.812500
2017-03-09 21:28:52.780979 LDAPM[491:46357] purgableMemory 12.562500
2017-03-09 21:28:52.780999 LDAPM[491:46357] resident_size 534

2017-03-09 21:28:55.454389 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:55.454456 LDAPM[491:46357] free 21.062500
2017-03-09 21:28:55.454476 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:55.454493 LDAPM[491:46357] used 1801.875000
2017-03-09 21:28:55.454511 LDAPM[491:46357] active 1070.968750
2017-03-09 21:28:55.454529 LDAPM[491:46357] inactive 540.312500
2017-03-09 21:28:55.454546 LDAPM[491:46357] wired 190.593750
2017-03-09 21:28:55.454564 LDAPM[491:46357] purgableMemory 10.906250
2017-03-09 21:28:55.454587 LDAPM[491:46357] resident_size 634

2017-03-09 21:28:58.757493 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:58.757688 LDAPM[491:46357] free 25.484375
2017-03-09 21:28:58.757710 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:28:58.757804 LDAPM[491:46357] used 1119.265625
2017-03-09 21:28:58.757830 LDAPM[491:46357] active 624.609375
2017-03-09 21:28:58.757848 LDAPM[491:46357] inactive 303.906250
2017-03-09 21:28:58.757866 LDAPM[491:46357] wired 190.796875
2017-03-09 21:28:58.757889 LDAPM[491:46357] resident_size 59

2017-03-09 21:29:00.488180 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:00.488253 LDAPM[491:46357] free 38.281250
2017-03-09 21:29:00.488273 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:00.488289 LDAPM[491:46357] used 1020.781250
2017-03-09 21:29:00.488308 LDAPM[491:46357] active 563.328125
2017-03-09 21:29:00.488326 LDAPM[491:46357] inactive 267.046875
2017-03-09 21:29:00.488343 LDAPM[491:46357] wired 190.406250
2017-03-09 21:29:00.488382 LDAPM[491:46357] purgableMemory 0.062500
2017-03-09 21:29:00.488416 LDAPM[491:46357] resident_size 126

2017-03-09 21:29:02.915796 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:02.915882 LDAPM[491:46357] free 46.578125
2017-03-09 21:29:02.915902 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:02.915917 LDAPM[491:46357] used 844.250000
2017-03-09 21:29:02.915935 LDAPM[491:46357] active 448.140625
2017-03-09 21:29:02.915952 LDAPM[491:46357] inactive 204.609375
2017-03-09 21:29:02.915970 LDAPM[491:46357] wired 191.500000
2017-03-09 21:29:02.915993 LDAPM[491:46357] resident_size 63

2017-03-09 21:29:05.359697 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:05.359786 LDAPM[491:46357] free 39.750000
2017-03-09 21:29:05.359810 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:05.359858 LDAPM[491:46357] used 730.562500
2017-03-09 21:29:05.359885 LDAPM[491:46357] active 363.437500
2017-03-09 21:29:05.359904 LDAPM[491:46357] inactive 176.109375
2017-03-09 21:29:05.359922 LDAPM[491:46357] wired 191.015625
2017-03-09 21:29:05.359979 LDAPM[491:46357] resident_size 53

2017-03-09 21:29:08.239699 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:08.239776 LDAPM[491:46357] free 36.656250
2017-03-09 21:29:08.239797 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:08.239813 LDAPM[491:46357] used 621.703125
2017-03-09 21:29:08.239831 LDAPM[491:46357] active 288.640625
2017-03-09 21:29:08.239848 LDAPM[491:46357] inactive 142.781250
2017-03-09 21:29:08.239865 LDAPM[491:46357] wired 190.281250
2017-03-09 21:29:08.240040 LDAPM[491:46357] resident_size 39

2017-03-09 21:29:11.167796 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:11.167871 LDAPM[491:46357] free 34.109375
2017-03-09 21:29:11.167893 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:11.168042 LDAPM[491:46357] used 638.437500
2017-03-09 21:29:11.168088 LDAPM[491:46357] active 311.562500
2017-03-09 21:29:11.168244 LDAPM[491:46357] inactive 149.296875
2017-03-09 21:29:11.168373 LDAPM[491:46357] wired 177.625000
2017-03-09 21:29:11.168492 LDAPM[491:46357] resident_size 85

2017-03-09 21:29:13.845295 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:13.845364 LDAPM[491:46357] free 41.937500
2017-03-09 21:29:13.845385 LDAPM[491:46357] TotalMemory 2048.000000
2017-03-09 21:29:13.845401 LDAPM[491:46357] used 637.250000
2017-03-09 21:29:13.845419 LDAPM[491:46357] active 321.453125
2017-03-09 21:29:13.845440 LDAPM[491:46357] inactive 145.171875
2017-03-09 21:29:13.845458 LDAPM[491:46357] wired 170.640625
2017-03-09 21:29:13.845522 LDAPM[491:46357] resident_size 152

2017-03-09 21:29:13.855866 LDAPM[491:46357] applicationDidReceiveMemoryWarning
Message from debugger: Terminated due to memory issue

可以发现在我们的 app 内存使用正常的情况下,free 内存、 active 内存以及 app 当前的物理内存变化都是很正常的,以 100 为单位变化。但是后期 app 要求分配过多内存的时候,发现 active 内存不再以 100 为额度增长,这个时候应该是操作系统基于 jetsam 机制开始杀死一些后台优先级低的 app 了。再进一步看,发现内存紧张的时候,purgeableMemory 不再打印了,是因为它的值变为了 0,所以我没有继续打印,也符合刚才的描述,在内存紧张时,可以自动释放的内存,这一手段也可以用来降低内存峰值。

Q & A

1.为什么 app 崩溃的时候,我的应用使用内存不多,但是系统剩余内存很少的情况下,首先杀掉了我的应用呢?

这个问题,我以前也比较好奇,觉得这个不符合常理。但是通过刚才的测试来看,在自身物理内存不断申请的情况下,当物理内存过大的时候,通过以上代码收集到的值并不是很大,反而变小了。可能是代码的问题,也可能是操作系统的问题。但是你所使用的代码或者第三方平台的收集策略很可能与此类似。收集到一个很小的值,但是并不代表你的 app 的内存使用情况很正确,很可能存在内存暴涨,但是收集结果却没有显示出来。

2. 为什么上面的数据 free memory + used memory (active + inactive + purgeableMemory) 的值不等于 TotalMemory 呢?

这个问题我也很好奇,而且会发现当你自身 app 内存申请不合理的时候 free memory + used memory 的值也会和正常情况下不同,目前只能猜测系统用这块内存来做了什么神秘的事情,如有对操作系统熟悉的同学,望告知!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,566评论 25 707
  • 概述 我们都知道一个进程是与其他进程共享CPU和内存资源的。正因如此,操作系统需要有一套完善的内存管理机制才能防止...
    SylvanasSun阅读 3,749评论 0 25
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 先来看下苹果文档: Memory Management Programming Guide for Core Fo...
    少少白阅读 4,258评论 4 18
  • 今天上午去姐姐家和大姨,一起聊天。姐姐说了一句最让我有感触的话那就是:“先干着吧,如果我要是你的话,对自己现...
    两条腿走路的喵阅读 319评论 0 0