1

Instruments学习之Allocations

 2 years ago
source link: https://www.jianshu.com/p/b617f16acb7f
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
12017.03.15 09:57:02字数 2,227阅读 7,478
注意:这个工具非常有用,文本也是一个超长文

Allocations初始界面

Allocations:检测一个进程(选择自己的app)内存分配和使用情况等
我们启动Allocations后得到一个初始界面

初始界面.png

简单说一下上图的3个地方
1:这里有两个部分了,因为官方说了Allocations(上面)和垃圾数据占用(下面)一起展示更好分析
2:一个列表,展示了哪些方法\部分消耗了多少内存,前面的钩钩上会在1部分显示出主柱状图,自己点一下就知道了,不截图
3:设置和扩展功能,文章后面慢慢讲

开始分析列表

我先随意的在自己app中点击,跳转等操作,然后截个图如下

分析图.png

我们可以惊讶的看到All Heap Allocations(真实内存)只有23.02,而All Anonymous VM(虚拟内存:为程序分配的虚拟内存,当程序有需要的时候,能够及时为程序提供足够的内存空间,而不会现用现创建)高达91.06,所以手机分配给我们的内存是114.08;我们现在不检测内存泄漏(是另外一个工具),所以我们尽量优化VM(因为不是app真实占用的内存,只是系统分配的),而VM主要由以下三部分组成(我会把三部分都优化完了后再运行截图)

VM:ImageIO_PNG_Data

关于这个问题我在google中找到了解释,说到

Replace:
background.image = [UIImage imageNamed:@"*.png"];
With:
background.image = [UIImage imageWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/*.png"]];
and now the ImageIO_PNG_Data's will be released when the view controller is dismissed.

我后面在我工程中搜索了一下imageNamed,确实有很多地方使用了,所以我们加载图片正确的思路应该是这样

1:对于大的图片且偶尔需要显示的应放到工程目录下,不要放到Assets.xcassets中;并使用imageWithContentsOfFile加载不让系统缓存
2:对于经常需要展示的小图片放到Assets.xcassets中让系统缓存,使用imageNamed加载

所以我改了一些地方,比如

imageView.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:nameArr[index] ofType:@"png"]];
//imageView.image = [UIImage imageNamed:nameArr[index]];

VM:CG raster data

关于这个问题我在google中找到了解释,这是SDWebImage的问题

* Decompressing images that are downloaded and cached can improve peformance but can consume lot of memory.
* Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.

所以我们需要在Appdelegate中设置一下

[[SDImageCache sharedImageCache] setShouldDecompressImages:NO];
[[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:NO];
[[SDImageCache sharedImageCache] setShouldCacheImagesInMemory:NO];

VM:CoreAnimation

这个问题在google搜索的时候没有明显的答案,我们先查看一下详细列表

进入详情列表.png
我们可以看到很多的地方都有这个VM问题,那我们具体看一下是哪些函数
具体查看调用栈.png
我们可以发现都是系统的一些方法,我们好像无从下手;这时候我们可以google看看发现有很多原因都会变成这个样子,其中一个是这里
Found out that animation caused by the inner pages.
Inside the pageViewController(viewController that added to the scrollView as a page) on viewWillDisappear:(BOOL)animated method I added this
for (CALayer* layer in [self.view.layer sublayers]) {
        [layer removeAllAnimations];
}
it resolved the problem.

其中的Found out that animation caused by the inner pages我们还是需要在扩展信息中查看能不能定位到某个页面(我不知道怎么定位,如果你知道请一定回复我),所以我在我刚才进入过的界面全部实现了如上方法

看看优化结果

这时候我们重新启动Allocations,然后重复上面随意的在自己app中点击,跳转等操作,然后截图如下

VM优化结果.png

我们发现一个好的现象VM:CG raster data明显减少了;同时我们得到一个坏现象,其他两项并没有明显变化,那我们继续分析什么原因,不过不要着急,我们先来看看右下方都有哪些功能

Record Settings

Launch Configuration for All Allocations

所有的Allocations启动如下配置:
Discard unrecorded data upon stop:当用户点击停止的时候丢弃没有记录的数据,勾不勾无所谓
Discard events for freed memory:当内存被释放的时候丢弃事件(也就是列表中不显示已被释放内存所关联的事件)
Only track VM allocations:只捕获虚拟内存的项,不勾,因为我们还是需要看看真实内存占用的
这里我们需要把第一、二个勾上

Launch Configuration for Heap Allocations

对所有的真实内存Allocations启动如下配置(如果你钩中了Only track VM allocations,这一栏是没办法操作的):
Record reference counts:记录引用计数
Identity virtual C++ objects:标记虚拟c++对象,这个钩上可以检测openGL等库
Enable NSZombie detection:检测僵尸对象,钩上可以发现有没有对已释放的对象发送消息

Recorded Types

这个我就不需要解释了,你需要捕获什么类型的事件就钩上,提供了简单的正则

Dispalay Settings

Track Display

决定上面将要显示什么内容

上面显示什么内容.png

Current Bytes:显示字节数量
Allocation Density:Allocation数量
Active Allocation Distribution:新激活的Allocation数量

Generation Analysis

这个功能是非常有用的,一般是这样用的:进入一个页面前mark一下,在退出这个页面的时候再mark一下可以比较哪些内容增加了,就可以具体分析哪些内存没有被释放;比如我们要进入日程界面的时候我点了一下mark

列表自动切换了.png

显示了Growth(相比上一次增加的量)为27.48,也就是第一次真实内存和虚拟内存之和;我们在日程界面操作一阵子之后我们再点击mark截图

再次mark.png
所以我们知道了我们退出日程界面内存依然还是增加了2.85,你可以点击查看具体是哪些增加了
哪些增加了.png

Allocation Lifespan

需要记录哪些Allocation
All Allocations:所有的
Created & Persistent:创建且存活的
Created & Destroyed:创建且被销毁的
我们目前只关心存活的,所以我们钩上第二个

Allocation Type

记录的Allocation类型
All Heap & Anonymous VM:所有真实内存和虚拟内存,我通常选这个分析
All Heap Allocations:所有真实内存
All VM Regions:所有分配过的虚拟内存
这里的选择将会影响到列表,比如我选择All VM Regions

只显示虚拟内存.png

Call Tree

这里的功能需要我们把列表展示类型切换成Call Trees,能够非常清晰的看到调用树

调用树展示.png

不过一般我们需要勾选一些选项,因为默认的实在看不出什么东西
Separate by Category:按照类别隔开,我们钩上看看效果

按照类别隔开.png
瞬间好看多了,我们能够清楚的看出来是哪些类别的VM
Separate by Thread:按照线程划分,我个人不是很喜欢这种划分,因为我不是很关心线程

Invert Call Tree:反转调用,我们给一张对比图就不需要解释了

未钩中.png
钩中.png
我习惯钩上,因为我能够一眼看到具体哪个方法出现了问题
Hide System Libraries:这个似乎是必钩的,因为我们目前只关心自己的方法,不关心系统的
Flatten Recursion:扁平化递归,我暂时还不知道是干嘛的,网上也没有搜到,不过我还是钩上了,听着是一个不错的功能

Call Tree Constraints

这个我就不需要讲了,是对列表中的数据进行过滤,可以是数量和大小;比如我只关心100以内的数据

100之内的数据png

Data Mining

数据挖掘,这是一个很具有噱头的功能;官网给了这么一个解释

Allows you to filter through the collected data for specific symbols and libraries.

就是可以过滤掉你不看的库、符号调用
点击Symbol、Library会自动把你选中的行的符号、库加到小框中

例子.png
符号和库有两个选项,就是是否过滤改行;点击Restore会去掉小框中的选中行,比如我们把帮帮管理助手去掉
去掉某行.png
我觉得我暂时用不到数据挖掘,所以我后面熟悉了的时候再来补充这部分

Extended Detail

这个我其实已经介绍过了,对于Allocations来说是看某一条数据的调用栈等信息
到现在我们只有一种列表展示没有介绍了,我们来看一眼

Allocations List类型.png

相对于Statistics来说多了调用库和方法的展示

刚才说到了,我们只对VM:CG raster data的优化表示满意,接下来我们继续优化VM:CoreAnimation和VM:ImageIO_PNG_Data

VM:ImageIO_PNG_Data优化

前面说到我们把加载大图片的方法换成了imageWithContentsOfFile但是效果不明显,现在我们就来看看具体是哪个方法产生了大量的虚拟内存,下面是步骤

找到调用函数.png
在谷歌借鉴了解决方法
//_pageScrollView是一个滚动视图,用来加载本地图片
...
//加入图片
    for (int index = 0; index < nameArr.count; index ++) {
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(index * pageScrollImagewidth, 0, pageScrollImagewidth, pageScrollImageheight)];
         NSString *imageFile = [NSString stringWithFormat:@"%@/%@.png",[[NSBundle mainBundle] resourcePath],nameArr[index]];
        @autoreleasepool {
             imageView.image = [UIImage imageWithContentsOfFile:imageFile];
        }
//        imageView.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:nameArr[index] ofType:@"png"]];
//        imageView.image = [UIImage imageNamed:nameArr[index]];
        [_pageScrollView addSubview:imageView];
    }
...
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    for (UIView *viewView in _pageScrollView.subviews) {
        [viewView removeFromSuperview];
    }
    _pageScrollView = nil;
}

按照这个方法后,效果还是很明显的,之前是33.95(看文章前面的图)

减少了很多.png

优化VM:CoreAnimation

我们同样用Generations检测出了这个问题

内存增加的主要内容.png

前面说到我们谷歌到可以采用下面的方法减少

for (CALayer *layer in self.view.layer.sublayers) {
        [layer removeAllAnimations];
    }

但是证明几乎没用,那我们只好继续谷歌看看,按照上面的方法,我们在谷歌输入关键字CA::Render::Shmem::new_shmem(unsigned long)进行搜索,我们从搜索的结果可以得知这是一个很多原因都会导致的结果;其中在这篇文章有两段描述

the solution is to reduce that space to an absolute minimum.
//把控件的范围设置到最小
...
I am having the same issue. I will try what you suggested with the size. 
But, maybe changing the background color from clearColor could 
correct the issue as well.
//改变视图的背景颜色

我试了改变背景颜色,没什么作用

[[UIView appearance] setBackgroundColor:[UIColor whiteColor]];
[[UIView appearance] setBackgroundColor:[UIColor clearColor]];

所以我们就暂时不解决这个了,我后面解决了会在这里进行补充,VM:Animation优化是一个大问题,可能需要一篇文章单独讲

看另一部分

接下来我们来看看这个视图是干什么用的,使用的时候需要手动捕获(或者你钩上Automatic Snapshotting自动定时捕获)一次左下角列表才有数据

捕获一次.png
默认会显示三种数据
Dirty Size:脏数据大小(没办法被重复使用)
Swapped Size:交换空间大小
Resident Size:固定数据大小
关于这三个名字的解释,你可以看看这里,所以我们得知这条数据是有异常的
异常数据.png
因为产生了31.54的垃圾数据,我们在这里找到了想要的答案,结果证明无效
又从这里得知我们不需要管这个问题,因为这时XCode工具自身的,不是我们程序的
所以我们来看下一个问题
下一个问题.png
我还特别重新启动了一次,证明我确实没有做什么其他操作,结果垃圾数据还是很多
malloc开头的.png
这是为什么呢?答案在这里,所以这一部分我们也不需要管,所以我们的程序没有出现大量垃圾数据情况,问题还是出在CoreAnimation
CoreAnimation问题.png
我们把这个问题留着, 因为和上面优化VM:CoreAnimation是一类问题,等我熟悉了我再补充

Regoins Map

Regoins Map视图.png

这个视图,主要是把每一条数据的地址段和调用路径给你显示出来了,我很少用这个视图,不过能看到path还是不错的

Allocations一般来做什么

其实文本已经大致讲了一下,Allocations对app优化非常有用,通常是拿来分析内存增加(不一定是内存泄漏)和app中各部分占用内存问题,当我们得知哪个内存占用比较多,我们直接进行优化即可减少内存占用问题
本文一直在讨论如何减少VM的占用,你接下来可以分析Heap占用


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK