macOS APP内存优化

概述

  1. 我们为什么减少内存占用?

    为了更好的用户体验

    内存是有限且系统共享的资源,一个程序占用更多,系统和其他程序所能用的就更少。程序启动前都需要先加载到内存中,并且在程序运行过程中的数据操作也需要占用一定的内存资源。减少内存占用也能同时减少其对 CPU 时间维度上的消耗,从而使不仅你所开发的 App,其他 App 以及整个系统也都能表现的更好。

  2. 可以减少的内存占用有哪些?

    从苹果的开发者文档里可以看到,一个 app 的内存分三类:

    • Leaked memory: Memory unreferenced by your application that cannot be used again or freed (also detectable by using the Leaks instrument).
    • Abandoned memory: Memory still referenced by your application that has no useful purpose.
    • Cached memory: Memory still referenced by your application that might be used again for better performance.

    之前在做后台开发的时候解决最多的问题就是Leaked memory,但是在客户端开发的时候会发现,Abandoned memory跟Cached memory渐渐地成为了内存消耗的主力

  3. 如何选取内存占用指标?

    • 系统APP 活动监视器-内存
    • terminal 使用footprint命令
    • objective-c 使用task_vm_info_data_t中的phys_footprint

    以上三种方式能得到相似的结果,也是WWDC2013 704 Building Efficient OS X Apps推荐的内存占用获取方式,附上最后一种方式的代码

        task_vm_info_data_t task_infos = {0};
        info_count = TASK_VM_INFO_COUNT;
        if (task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&task_infos, &info_count) == KERN_SUCCESS) {
            memory_footprint = task_infos.phys_footprint;
        }
    

分析工具

  1. 静态代码扫描

    • Xcode自带Analyze
      适合检查leaked memory,对malloc或者new方式分配的内存或者未定义的变量等检查效果较好
  2. MLeaksFinder

    • 适合检查如NSViewController被释放了了,但它的view没被释放,或者一个NSView被释放了了,但它的某个subview没被释放。对手机上频繁切换页面的APP优化效果明显。
  3. 微信内存监控

    Matrix-iOS 当前工具监控范围包括:崩溃、卡顿和爆内存,包含以下两款插件:

    • WCCrashBlockMonitorPlugin: 基于 KSCrash 框架开发,具有业界领先的卡顿堆栈捕获能力,同时兼备崩溃捕获能力。
    • WCMemoryStatPlugin: 一款性能优化到极致的爆内存监控工具,能够全面捕获应用爆内存时的内存分配以及调用堆栈情况。

    内存这块主要使用WCMemoryStatPlugin,得到的内存分配文件相比较与Instruments有部分数据缺失,需要自己编写脚本符号化得到的内存分配文件,适合监控线上的内存情况,可以发现一些偶现的内存问题

  4. Instruments leaks

Instruments-Leaks-Test.jpg

- 可以看到单纯的内存泄露并不多,看来大都属于在运行过程中不断的申请内存,但是在很长一段时间内没有释放,或者是直到程序退出时,才释放申请的内存。

  1. Instruments Allocations
    • 可以看到自应用开始详细的内存分配,不过占用空间过大,不能持续运行。
  2. Memory Graph
    • 它对比Instruments Allocations,开启后不会迅速产生大量日志文件,导致应用程序卡死。而且他提供丰富的命令,几乎涵盖了,Instruments中Allocations跟leaks的所有功能,并且使用命令对当前应用程序中分配的内存类型等进行简单的统计分析,快速的定位应用程序中分配最多的内存是什么,以及是如何分配的很方便。

内存优化的方法论

  1. 在介绍了上述几种工具之后,我还希望可以借助一些工具,对内存占用进行一些分析操作,比如对分配的内存进行分类,排序,查看其分配堆栈,查看其被谁持有。
    左图就是由此产生的内存问题分析方法,具体操作是打开APP,持续使用,期间可以随时导出Memory Graph文件,使用VMMap查看文件,对文件中的内存分配进行分类排序,取其中分配最大的一块内存的首地址使用Malloc_History跟Leaks进行分析,可以得到这块内存分配的详细堆栈以及当前是被谁引用,接下来就是对这块内存进行优化,通过不断的优化当前内存分配的大头,带来APP内存的巨大下降。


    methodology.jpg

通过上述方法进行分析,发现APP中内存占用几个大类

  1. 界面的渲染
  2. 图片的压缩
  3. 各种缓存
  4. bug

方案

接下来我们逐个分析,介绍APP的内存占用

界面的渲染

  1. view的定制通过重写drawrect消耗大量内存
  • 为何重写drawrect消耗大量内存
    当我们重写drawrect时,会促使Core Animation创建一个Open GL纹理,并将你使用CoreGraphics框架的绘图操作数据放到纹理的位图数据中。

    backing-store.png

    计算机系统中 CPU、GPU、显示器是以上面这种方式协同工作的。CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。当我们重写drawrect时,CPU会创建backing-store进行渲染,随后拷贝到GPU的显存(VRAM)
    drawrect.png

    上图就是一个BackingStore的绘制过程,首先是window窗口触发重绘,准备重绘区域等大的一个基于bitmap的上下文,然后从父view开始,遍历所有的子view进行层层绘制,每一个绘制操作完成之后,才开始下一个绘制。
    观察drawRect方法,如发现是一些点线条,背景色等的绘制,我们就可以使用CAShapeLayer+CGPath的方式进行绘制,CAShapeLayer是苹果提供的一个对opengl es的一个封装,可以完全满足我们的需要。


    CAShapeLayer.png

    上图就是使用CAShapeLayer+CGPath的绘制方式替换之前通过Core Graphics的绘制方式的流程对比,一个CAShapeLayer 不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。并且CAShapeLayer渲染快速,使用了硬件加速,绘制同一图形会比用Core Graphics快很多。

  • 离屏渲染介绍
    首先创建屏幕外缓冲区,然后渲染到纹理中,最后将结果渲染到帧的缓冲区,其中涉及到两次昂贵的环境转换(转换环境到屏幕外缓冲区,然后转换环境到帧缓冲区)。可以直接将图层合成到帧的缓冲区中(在屏幕上);当帧缓冲区图片被复用的时候,可以提升性能。
    • 帧缓冲区介绍
      帧缓冲区(显存或者内存上一段空间):是由像素组成的二维数组,每一个存储单元对应屏幕上的一个像素,整个帧缓冲对应一帧图像即当前屏幕画面。帧缓冲通常包括:颜色缓冲,深度缓冲,模板缓冲和累积缓冲。这些缓冲区可能是在一块内存区域,也可能单独分开,看硬件。
  • Core Animation介绍
    Core Animation的核心是OpenGL ES的一个抽象物,Core Animation的layer对应着OpenGL ES的texture,Core Animation可以有子layer,所以我们能看到他是一个图层树。在图像显示过程中,Core Animation的主要任务是判断出哪些图层需要被(重新)绘制,然后交由OpenGL ES将这些图层合并、显示到屏幕上。

图片的压缩

  1. 减少图片压缩产生的内存占用峰值

    • 图片的压缩逻辑改造
      使用ImageIO方式对图片进行压缩,无需解码bitmap
      image-io.png

      下面附上实现代码
        NSMutableDictionary* options = [NSMutableDictionary dictionary];
    
        [options safeSetObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kCGImageSourceCreateThumbnailFromImageAlways];
        [options safeSetObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kCGImageSourceCreateThumbnailWithTransform];
        [options safeSetObject:(__bridge id)kCFBooleanFalse forKey:(__bridge id)kCGImageSourceShouldCache];
        [options safeSetObject:[NSNumber numberWithFloat:maxPixelSize] forKey:(__bridge id)kCGImageSourceThumbnailMaxPixelSize];
    
        CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
    

各种缓存

  1. 减少APP内缓存
    • 减少单个缓存的大小
    • 减少缓存时间,及时释放
    • 减少缓存数量

bug

  1. bug修复

推荐阅读:

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