IOSiOS开发 Objective-CiOS开发

Instruments使用总结

2018-12-11  本文已影响25人  音符上的码字员

一、Instruments介绍

Instruments 一个很灵活的、强大的工具,是性能分析、动态跟踪 和分析OS X以及iOS代码的测试工具,用它可以极为方便收集关于一个或多个系统进程的性能和行为的数据,并能及时随着时间跟踪而产生的数据,并检查所收集的数据,还可以广泛收集不同类型的数据.也可以追踪程序运行的过程,这样instrument就可以帮助我们了解用户的应用程序和操作系统的行为。

 总结一下instrument能做的事情:
 1. Instruments是用于动态调追踪和分析OS X和iOS的代码的性能分析和测试工具;
 2.Instruments支持多线程的调试;
 3.可以用Instruments去录制和回放,图形用户界面的操作过程
 4.可将录制的图形界面操作和Instruments保存为模板,供以后访问使用。

 instrument还可以:
 1.追踪代码中的(甚至是那些难以复制的)问题;
 2.分析程序的性能;
 3.实现程序的自动化测试;
 4.部分实现程序的压力测试;
 5.执行系统级别的通用问题追踪调试;
 6.使你对程序的内部运行过程更加了解。

打开方式:
Xcode -> Open Developer Tool -> Instruments

1.png

其中比较常用的有四种:

其他的:

二、Allocations(分配)

1.内存分类:

2.Abandoned memory

其中内存泄漏我们可以用Leaks,野指针可以用Zombies(僵尸对象),而在这里我们就可以用Allocations来检测Abandoned memory的内存。

2.png

即我们采用Generational Analysis的方法来分析,反复进入退出某一场景,查看内存的分配与释放情况,以定位哪些对象是属于Abandoned Memory的范畴。
在Allocations工具中,有专门的Generational Analysis设置,如下:

3.png

我们可以在程序运行时,在进入某个模块前标记一个Generation,这样会生成一个快照。然后进入、退出,再标记一个Generation,如下图:

4.png

在详情面板中我们可以看到两个Generation间内存的增长情况,其中就可能存在潜在的被遗弃的对象,如下图:

5.png

其中growth就是我们增长的内存,GenerationA是程序启动到进入该场景增长的内存,GenerationB就是第二次进入该场景所增长的内存,查看子类可以发现有两个管理类造成了Abandoned memory

3.设置Generations

使用instrument测试内存泄露 工具 Allocations 测试是否内存泄露 使用标记,可以更省事省力的测试页面是否有内存泄露
1)设置Generations

6.jpg

2)选择mark generation

7.jpg

3)使用方法 在进入测试页面之前,mark一下----->进入页面----->退出----->mark------>进入------->退出------->mark------>进入如此往复5、6次,就可以看到如下结果

8.jpg

这种情况下是内存有泄露,看到每次的增量都是好几百K或者上M的,都是属于内存有泄露的,这时候就需要检测下代码一般情况下,100K以下都属于正常范围,growth表示距离你上次mark的增量

三、Leaks(泄漏)

1.内存溢出和内存泄漏的区别

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak会最终会导致out of memory!

在前面的ALLcations里面我们提到过内存泄漏就是应该释放而没有释放的内存。而内存泄漏分为两种:Leaked MemoryAbandoned Memory。前面我们讲到了如何找到Abandoned Memory被遗忘的内存,现在我们研究的就是Leaked Memory

发生的方式来分类,内存泄漏可以分为4类:

常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

影响:从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。

下边我们介绍Instruments里面的Leaked的用法,首先打开Leaked,跑起工程来,点击要测试的页面,如果有内存泄漏,会出现下图中的红色的❌。然后按照后边的步骤进行修复即可

1.png 2.png

上面的旧版的样式,下面的是新版的样式,基本操作差不多

Snip20181211_2.png 3.png

在详情面板选中显示的若干条中的一条,双击,会自动跳到内存泄露代码处,然后点击右上角 Xcode 图标进行修改。

下图是对Leaked页面进一步的理解:

4.png

内存泄漏动态分析技巧:

  1. 在 Display Settings 界面建议把 Snapshot Interval (snapʃɒt, 数据快照)间隔时间设置为10秒,勾选Automatic Snapshotting,Leaks 会自动进行内存捕捉分析。(新版本直接在底部修改)
  2. 熟练使用 Leaks 后会对内存泄漏判断更准确,在可能导致泄漏的操作里,在你怀疑有内存泄漏的操作前和操作后,可以点击 Snapshot Now 进行手动捕捉。
  3. 开始时如果设备性能较好,可以把自动捕捉间隔设置为 5 秒钟。
  4. 使用ARC的项目,一般内存泄漏都是 malloc、自定义结构、资源引起的,多注意这些地方进行分析。
  5. 开启ARC后,内存泄漏的原因
    开启了ARC并不是就不会存在内存问题,苹果有句名言:ARC is only for NSObject。

注:如果你的项目使用了ARC,随着你的操作,不断开启或关闭视图,内存可能持续上升,但这不一定表示存在内存泄漏,ARC释放的时机是不固定的。

这里对 Display Settings中 的 Call tree 选项做一下说明 [官方user guide翻译]:

四、Time Profiler(时间分析器)

用来检测app中每个方法所用的时间,并且可以排序,并查找出哪些函数占用了大量时间。

使用Time Profile前有两点需要注意的地方:
1、一定要使用真机调试

在开始进行应用程序性能分析的时候,一定要使用真机。因为模拟器运行在Mac上,然而Mac上的CPU往往比iOS设备要快。相反,Mac上的GPU和iOS设备的完全不一样,模拟器不得已要在软件层面(CPU)模拟设备的GPU,这意味着GPU相关的操作在模拟器上运行的更慢,尤其是使用CAEAGLLayer来写一些OpenGL的代码时候,这就导致模拟器性能数据和用户真机使用性能数据相去甚远

2、应用程序一定要使用发布配置

在发布环境打包的时候,编译器会引入一系列提高性能的优化,例如去掉调试符号或者移除并重新组织代码。另iOS引入一种"Watch Dog"[看门狗]机制,不同的场景下,“看门狗”会监测应用的性能,如果超出了该场景所规定的运行时间,“看门狗”就会强制终结这个应用的进程。开发者可以crashlog看到对应的日志,但Xcode在调试配置下会禁用"Watch Dog"

1)界面详情:

1.png

2)详细面板

5.png

主要是看Call Tree和Sample List这两种视图:

3)调用树

4.png

Running Time:函数运行的时间,这个时间是累积时间
Self:在栈顶次数
Symbol Name:被调用函数的符号信息

4)详情面板更多的信息选项

3.png

5)样本列表

2.png

五、Zombies(僵尸)

1.概念

翻译英文:专注于检测过度释放的“僵尸”对象。还提供了数据对象分配的类以及所有活动分配内存地址的历史。

这里我们可以看到一个词语叫“over-release”,过度释放。我们在项目中见到最多的就是“EXC_BAD_ACCESS”或者是这样的:Thread 1: Program received signal:"EXC_BAD_ACCESS",这就是访问了被释放的内存地址造成的。

过度释放,是对同一个对象释放了过多的次数,其实当引用计数降到0时,对象占用的内存已经被释放掉,此时指向原对象的指针就成了“悬垂指针”,如若再对其进行任何方法的调用,(原则上)都会直接crash(然而由于某些特殊的情况,不会马上crash)。过度释放简单的说就是对release的对象再release,就是过度释放

我们需要知道这几个概念:

1、内存泄漏:对象使用完没有释放,导致内存浪费。
2、僵尸对象:已经被销毁的对象(不能再使用的对象)
3、野指针:指向僵尸对象(不可用内存)的指针。给野指针发消息会报EXC_BAD_ACCECC错误
4、空指针:没有指向储存空间的指针(里面存的是nil,也就是0)。在oc中使用空指针调中方法不会报错。

注意:为了避免野指针错误的常见方法:在对象被销毁之后,将指向对象的指针变为空指针。

对于过度释放的问题,可以直接使用Zombie,当过度释放发生时会立即停在发生问题的位置,同时结合内存分配释放历史和调用栈,可以发现问题。至于上文提到的不会crash的原因,其实有很多,比如:

对象内存释放时,所用内存并没有完全被擦除,仍有旧对象部分数据可用

原内存位置被写入同类或同样结构的数据

2.原理

我们将僵尸对象“复活”的目的:僵尸对象就是让已经释放了的对象重新复活,便于调试;是为了让已经释放了的对象在被再次访问时能够输出一些错误信息。其实这里的“复活”并不是真的复活,而是强行不死:这么说吧 相当于 他的RC=0的时候 系统再强行让他RC=1,顺便打上一个标记 zoom,等到你去掉那个沟以后 系统会把带有标记zoom的对象RC=0。

3.用法

下边是Instruments里面的Zombies的用法:

在Launch Configuration中勾选Record reference counts和Enable NSZombie detection。其中Recordreference counts是显示引用计数,Enable NSZombie detection是能够检测僵尸对象。

1.png

这样在程序运行的时候,如果发现僵尸对象它就会弹出一个对话框,点击其中“→”按钮,在屏幕的下方会显示僵尸对象的详细信息,下图可以看到僵尸对象的引用计数变化情况。

2.png

注意:Zombies模版在使用的时候会导致内存的飙升,这是因为所有被释放的对象被僵尸对象取代,并未真的释放掉,在结束Zombies时会释放,这是预知行为,这就意味着instrument里的其它工具和Zombies是不能同时使用的,Zombies会导致其它的数据不准。包括leaks,你也不应该把它加到Zombies模版中,即使这么做了结果也没什么意义。对于iOS应用来说,在用Zombies模版时使用iOS模拟器比真机要好

另外XCode也提供了手动设置NSZombieEnabled环境变量的方法,不过设置NSZombieEnabled为True后,会导致内存占用的增长,同时会影响Leaks工具的调试,这是因为设置NSZombieEnabled会用僵尸对象来代替已释放对象。

点击Product菜单Edit Scheme打开该页面,然后勾选Enable Zombie Objects复选框:

3.png

最后提醒的是NSZombieEnabled只能在调试的时候使用,千万不要忘记在产品发布的时候去掉,因为NSZombieEnabled不会真正去释放dealloc对象的内存,一直开启的话,该死去的对象会一直存在,后果可想而知,自重!

六、扩展




好了,以上就是对于Instruments工具的使用总结
喜欢的就点个赞👍吧

参考文章

Allocations:Xcode8 Instruments 测试工具使用三
Leaks:Xcode 8 Instruments 学习(一)
Time Profiler:Xcode 8 Instruments 学习(二)
Zombies:Instruments性能检测

上一篇下一篇

猜你喜欢

热点阅读