如何用 Instruments 检测 iOS 每个查看进程消耗内存的电量消耗

5588人阅读
为了节省大家的时间,提供一个演示的Demo给大家。.
下载后解压然后用Xcode打开。编译运行APP后 然后在搜索框内输入任意词汇,点击结果你会看到下面的结果
正如你所见的,这个app很简单.程序其实调用的是Flickr的API,通过app顶部的搜索框执行搜索后在下面的tableview显示你搜索的搜索词,搜索词后面的括号内有搜索结果的个数,点击此行进入一个缩略图的结果列表页面 如上图. 点击其中一行 进入图像的大图模式,在这个页面你可以根据需要旋转图像。
&到目前为止页面看起来差不多了,你也许会想应该可以直接提交appstore了吧.接下来这篇文章将会教你instruments工具来提高你app性能和稳定性.
“时间探测器”
天下武功,唯快不破。很多公司都信奉这个教条.恨不得把app压法周期压缩到最低,这就导致了开发中隐藏了很多问题,有点经验的工程师草率的优化下,更糟的情况那些没有经验的工程师甚至不会对app进行任何优化.
某种程度上来说,你开发过程中是可以忽略性能优化的. 十年前,移动设备的硬件资源是非常有限的.甚至连浮点数都是被禁止的.因为浮点数能导致代码变大计算的速度变慢.
科技发展如此迅速的今天,硬件很大程度上可以弥补软件的短板.现在的移动设备3D硬件处理的效率甚至媲美于PC机了,但是你不能总依赖于硬件和处理器速度来掩饰你APP做的多垃圾吧.(如果安卓系统跑在Iphone上还能够像iOS一样顺滑吗?其实是一个道理的)
性能这个概念很抽象,所以我们必须借助数据化图形化的输出方式.你可能花一周的时间去优化一个有趣的算法,但是这算法只占总执行时间的0.5%,不管你花多少精力去优化它,没人会注意到.相反一个for循环花费了90%的时间,你稍微修改下就能提高10%的效率,就是这个简单的修改可以得到大家很大的好感.因为.他们运行app时的第一感受就是比之前快了很多.没人会care你修改的是一个多牛逼的算法,还是一个简单的for循环.
这个说明了什么?
与其花费时间在优化小细节上不如多点时间找到你改优化的地方.
下面引出第一个工具 Time Profiler,可以测量时间的间隔,中断程序执行,跟踪每个线程的堆栈.你可以想象下是Xcode调试时按下暂停时的画面
比如,100个样本都在做1毫秒的间隔,然后在某个方法堆栈顶部有10个样本,你可以推算出大概的时间有10%个10毫秒花费在此方法中,这是一个近似值.
从Xcode的菜单选择Product-Profile,或者选择?
程序会启动Instruments,这时候你会看到一个选择窗口
这是instruments所有测试仪器的面板,选择 “timer profilter” 点击“profile”回启东模拟器和app,此时会要求你输入一次密码,以便instruments能有权限去截获监听此进程。
在工具窗口中,可以看到时间计数,并留下了一个小箭头移动到右侧的图形在屏幕的中央上方。这表明该应用程序正在运行。
现在开始运行app,搜索一些图片,这时候你发现查找一个结果太慢了,而且搜索结果列表页面滚动起来也是让人无法忍受的。
首先,确保工具栏中的视图选择有选择的所有三个选项,如下所示:&
这将确保所有的面板都打开。现在,研究下面的截图和它下面的每个部分的解释:
1. 录控按钮。中间的红色按钮将停止与启动它被点击时,应用程序目前正在分析。注意这实际上是停止和启动应用程序,而不是暂停它。 &
2. 运行定时器和运行导航,定时器显示APP已经运行了多长时间,箭头之间是可以移动的。如果停止,然后使用录制按钮重新启动应用程序,这将开始一个新的运行。显示屏便会显示“run2 of 2”,你可以回到第一次运行的数据,首先你停止当前运行,然后按下左箭头回去。 &
3. 运行轨道。
4. 扩展面板,在时间探查仪器的情况下,它是用来跟踪显示堆栈。 &
5. 详细地面板。它显示了你正在使用的仪器的主要信息,这是使用频率最高的部门,可以从它这里看到cpu运行的时间 &&
6. 选项面板,稍后介绍。
执行图像搜索,并深究结果。我个人比较喜欢寻找“狗”,当然你也可以选择任意你想要的内容。比如猫啊美女啊什么的。
现在上下滚动记下列表,让时间探测器测量下数据,然后注意看下屏幕的变化和数值。这些数值反应了CPU周期。
但是你也许会发现下面的数值太多,看你的眼花缭乱。下面打开左边的调用树 然后按着如下的配置
以下介绍下配置选项:
Separate by Thread: 每个线程应该分开考虑。只有这样你才能揪出那些大量占用CPU的&重&线程 &
Invert Call Tree: 从上倒下跟踪堆栈,这意味着你看到的表中的方法,将已从第0帧开始取样,这通常你是想要的,只有这样你才能看到CPU中话费时间最深的方法.也就是说FuncA{FunB{FunC}} 勾选此项后堆栈以C-&B-A 把调用层级最深的C显示在最外面&
Hide Missing Symbols: 如果dSYM无法找到你的app或者系统框架的话,那么表中看不到方法名只能看到十六进制的数值,如果勾线此项可以隐藏这些符号,便于简化数据
Hide System Libraries: 勾选此项你会显示你app的代码,这是非常有用的. 因为通常你只关心cpu花在自己代码上的时间不是系统上的
Show Obj-C Only: 只显示oc代码 ,如果你的程序是像OpenGl这样的程序,不要勾选侧向因为他有可能是C++的 &
Flatten Recursion: 递归函数, 每个堆栈跟踪一个条目
Top Functions: 一个函数花费的时间直接在该函数中的总和,以及在函数调用该函数所花费的时间的总时间。因此,如果函数A调用B,那么A的时间报告在A花费的时间加上B.花费的时间,这非常真有用,因为它可以让你每次下到调用堆栈时挑最大的时间数字,归零在你最耗时的方法。
如果您已启用上述选项,虽然有些值可能会略有不同,下面的结果的顺序应该是类似下表:
通过上面你能看到大部分时间都花在更新表格照片了.
双击此行,然后将会看到如下
那么这很有趣,不是吗!几乎四分之三的时间花费在setPhoto:方法都花在创造照片的图像数据!
现在可以看到的是什么问题,NSData’s dataWithContentsOfURL 方法并不会立即返回,因为要从网上去数据,每次调用都需要长达几秒的时间返回,而此方法运行在主线程,可想而知会有什么结果了.
其实为了解决这个问题,类提供了一个ImageCache 的后台异步下载的方法.
现在,您可以切换到Xcode和手动找到该文件,但仪器有一个方便的“打开Xcode中”按钮,就在你的眼前。找到它的面板只是上面的代码并单击它:
想如下修改
修改好后,在仪器重新运行该应用程序Product—Profile(或cmd-记住,这些快捷键真的会为您节省一些时间)。
请注意,这个时候会再问一次你是否使用一起。这是因为你还有一个窗口中打开这个程序,及仪器假定您要使用相同的选项再次运行。
执行一些更多的搜索,并注意此时用户界面不是那么卡顿了!这些图像现在异步加载,并缓存在后台,所以一旦他们已经被下载一次,他们不必再次下载。
看上去很不错!是时候发布了吗? 当然还不够
分配,分配,分配
接下来的仪器是分配工具。它能给出你所有创建和存储它们的内存的详细信息,它也显示你保留了每个对象的计数。
关闭仪器,回到Xcode和选择Product-&Profile。然后,从选择器分配并单击配置文件。如下图:
程序再次打开 然后你会看到
这个时候你会发现两个曲目。一个叫(分配)Allocations,以及一个被称为VM Tracker(虚拟机跟踪)。该分配轨道将详细在本教程中讨论;虚拟机跟踪也是非常有用的,但更复杂一点。
所以你的错误会追踪下?
有隐藏的项目,你可能不知道有东西在那儿。你可能已经听说了内存泄漏。但你可能不知道的是,其实有两种泄漏。
第一个是真正的内存泄漏,一个对象尚未被释放,但是不再被引用的了。因此,存储器不能被重新使用。
第二类泄漏是比较麻烦一些。这就是所谓的“无界内存增长”。这发生在内存继续分配,并永远不会有机会被释放。
如果永远这样下去你的程序占用的内存会无限大,当超过一定内存的话 会被系统的看门狗给kill掉.
建立一个场景,你可以检测出无限的内存增长。首先,在应用程序使10个不同的搜索(不要用已经存在的搜索)。确保搜索的一些结果!现在让程序等待几秒钟。
你应该已经注意到,在分配的轨道图不断上升。这是告诉你的,内存被分配了。它的这一特征,将引导你找到无限的内存增长。
你将要执行的是“heap shot analysis”。为此,按这个按钮叫“Mark Heap”。你会发现的详细面板左侧的按钮
按下它,你会看到一个红色的标志出现在轨道上,像这样:
heap shot分析的目的是执行一个动作多次,看看如果内存是否无限增长。搜索一个内容,稍等几秒加载图像,然后返回主页。然后再标记堆。反复这样做不同的搜索。试过几个搜索后,仪器会看起来像这样:
这时你应该会疑问。图中的蓝色是怎么回事了,你继续这样操作10次这样的搜索 蓝色还不断变高:
那肯定是不好的。别急,有什么关于内存的警告?你知道这些,对不对?内存警告是告诉一个应用程序,内存警告是ios处理app最好的方式尤其是在内存越来越吃紧的时候,你需要清除一些内存。
内存一直增长其实也不一定是你的代码除了问题,也有可能是UIKit 系统框架本身导致的.
通过选择HardwareSimulate内存警告在iOS模拟器的菜单栏模拟内存警告。你会发现,内存使用量出现小幅回落,但绝对不会回到它应该的。所以还是有无限的内存增长发生的地方。&
在iOS模拟器的菜单栏中选择hardwaresimulate内存警告模拟内存警告。你会发现内存使用会出现小幅回落,但肯定不会回到它应该在的地方。
每一次的搜索后做你可以看到内存已拍摄之间的分配。在详细信息面板看一看,你会看到一好多堆镜头。
第一个堆镜头作为参照,然后随便打开一个堆镜头,你会看到如下:
靠,这是一个很大的对象!从哪里开始看呢?
最好的方式是通过列表,你在你的应用程序直接使用的类。在这种情况下,HTTPHeaderDict,CGRegion,CGPath,CFNumber,等等都是可以忽略了。
但是,一个突出的是UIImage,这肯定是在你程序使用的。点击上的UIImage左侧的箭头显示的完整列表。选择一个,在扩展详细信息面板:
图中灰色的是系统库,黑色部分是你应用的代码,要获得此跟踪更多的上下文,双击唯一的黑框ImageCache方法,这时候将掉转到如下方法
工具是非常有用的,你现在要努力通过自己的代码思考发生了什么.看看通过上面的方法,你会看到它调用一个名为setImage方法:forKey:。这种方法在缓存以防它再次使用以后的应用程序的图像。
一起来看看该方法的实现:
从网络上下载一个图片添加字典中,你会注意到这些图片从来没有从字典清楚过。
这就是内存为什么会一直增长,因为应用程序并不会从缓存里删除东西.它只会一直增加他们。
要解决此问题,你需要的是ImageCache收到UIApplication内存吃紧的警告时.清理缓存。
为了使ImageCache能接收通知,修改init方法如下:
注册UIApplicationDidReceiveMemoryWarningNotification执行memoryWarning:方法。
memoryWarning删除缓存中的所有对象。这将确保没有持图像。
为了测试此修复程序,再次启动仪器(从Xcode中有cmd)和重复的步骤。不要忘了在模拟结束内存警告!
注意:请确保您从Xcode中退出,重新构建,而不是仅仅点击仪器仪表上的红色按钮,以确保您使用的是最新的代码。
这一次分析应该是这样的:
这个时候,内存受到内存警告后急剧下降。但还是有一些内存整体增长,但远不及像以前那样。
究其原因还是有一定的增长确实是由于系统库,并没有太多可以做的。看来,系统库不释放所有的内存,这可能是由设计或可能是一个错误。你可以在你的应用程序做的是释放尽可能多的内存越好,你已经做到这一点!
干得好!还有一个问题,修补了, - 仍然有泄漏,你还没有解决的第一种类型的问题。
内存泄漏的仪器。这是用来找到第一类泄漏前面提到的 - 当一个对象不再被引用时出现的那种。
检测泄漏是可以理解的一个很复杂的事情,但泄漏的工具记得,已分配的所有对象,并定期通过扫描每个对象以确定是否有任何不能从任何其他对象访问的。
关闭仪器,回到Xcode和选择Product-&Profile
回到你的应用程序!执行搜索,得到结果。然后点选结果的预览行打开全屏浏览器。按下旋转按钮在左上角,然后再按一次。
回到仪器,等待片刻。如果你已经正确地完成上述步骤后,你会发现泄漏已经出现了!你的工具窗口将看起来像这样:
返回到模拟器,并按下旋转几次。然后返回到仪器和等会,得到如下结果:
哪来的泄漏从哪里来?扩展详细信息面板
在扩展的详细信息面板打开CGContext上名单。在列表中选择CGContext上的元素之一,这表明导致要创建的对象,如下面的堆栈跟踪:
再次,涉及到你的代码中的帧显示为黑色。由于只有一个,双击它,看看代码的方法。
有问题的方法是rotateTapped: ,这是被调用时被窃听旋转按钮的处理程序。这种方法旋转原始图像,并创建一个新的图像,如下:
再次,仪器只能在这里给你一个提示,问题出在哪里,它不能告诉你确切位置的泄漏。这是唯一能够证明你在创建对象泄露的地方.你可能认为ARC并有不可能是造成代码中内存泄漏…对不对?
回想一下,ARC只涉及Objective-C的对象。它不管理保留和的CoreFoundation对象而不是Objective-C的对象的释放。
啊,现在它开始变得明显的问题是什么?
-CGContextRef和CGImageRef对象永远不会被释放!为了解决这个问题,在rotateTapped方法的末尾添加以下两行代码:
这两种调用都需要来维护这两个对象的保留计数。这个说明,你还需要了解引用计数 - 即使你在你的项目中使用的ARC!
从在Xcode中,使用cmd工具构建和运行应用程序。
在使用泄漏仪器仪器再看看应用程序,看看是否泄漏的被固定。如果你正确地遵循上述步骤,泄漏应消失!
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:81510次
积分:1343
积分:1343
排名:千里之外
原创:47篇
阅读:17498
(2)(1)(1)(2)(2)(2)(3)(3)(1)(5)(3)(14)(17)作为一个iOS程序员,你是否会使用Instruments
简单调查一下
如果会的话,你用它做过什么?
共10条回复
最主要的就是profiler 和内存泄漏测试了吧
根本没法想象有人能不用,当然,可能因为我经常用xcode写c++
@ 事实上,有很多人甚至不知道
会用。当然, 年初刚学的时候也还不知道这个东西,6月份看WWDC的时候才知道,Google一番才懂的用。
偶尔程序内存占用不正常的时候, 就会用下, 其它时候不用.
我的一个小游戏 App 玩了一会手机就很热,用了 Profiler 才发现有一个动画无限循环(尽管已经进入别的页面),导致 CPU 100%
查看内存泄露,查看页面流畅度。。。(不是很会用啊)
MRC情况下,Instruments简直时时都在用,不过功能用不完全,既然写到这了,@ 写一个文档吧
@ 你也可以写一个啊
性能优化必须要用把
玩过一点点自动化测试下次自动登录
现在的位置:
& 综合 & 正文
IOS:Instruments 使用教程
原文:Instruments User Guide
PDF文档下载:
【翻译缘由---翻译本身也是强化学习的过程】
之前一直在CocoaChina上面潜水,虽然做iOS开发也快2年了,但是总觉得缺少了点什么。以前查看英文API文档,有些细节总是记不住,每次看完之后也没什么印象。忽然有一天有人向我抱怨说查看官方英文API文档太痛苦了,然后我就想如果我把一些常用的官方文档翻译为中文的话,或许可以帮助这批需要帮助的人,然后偶就心血来潮,开始了自娱自乐的翻译之旅。帮助别人的同时,自己也获得快乐。
或许很多人对Instruments应用不太了解,但可能很多老的iOS开发者都应该用过Instruments工具来检测iOS应用内存泄漏情况。特别是在iOS 5.0之前,即苹果在iOS平台上面还没支持ARC的时候,写iOS应用就类似C语言那样,容易忘记释放内存,而内存对移动设备而言是非常可贵的。即使目前iPhone设备内存已经基本都满足512MB了,但是因为苹果的后台模式是把整个应用封装起来等待下次启用,所以该应用所占用的内存同样被占据了。也就是即使应用进入后台模式,它还是仍然占用原先的内存的,所以你打开的应用越多,内存耗用自然也很多。对很多普通用户而言,往往他们打开的应用都是进入后台模式的,很少有用户清理后台的应用,所以也就造成很多应用其实可用内存还是非常有限地
(题外话:如果苹果原生支持一键清理后台程序就好了,貌似越狱的工具里面有这样的支持的)。
还有做过iOS应用自动化测试的开发者,应该对UIAutomation很熟悉吧。对,它就是通过JS脚本来写界面自动化测试用例。而Instruments应用对UIAutomation支持很完善,你可以通过它查看很多代码潜在的问题,并测试性能。
其实Instruments应用还有很多强大的功能,它原生支持很多instrument工具,帮助你分析你的代码,不仅包括内存检测和自动化测试,它还可以监测文件读写操作等等待。所以一个好的iOS开发者是应该掌握Instrument应用的使用。因为Instruments应用本身功能太强大的,所以完全掌握机会不可能,但是因为它们内置的很多工具具有相似性,所以你基本掌握自己常用的即可。同时了解一下内部有哪些功能,这样在你需要用到的时候再查查文档,就可以很快上手了。
最后,本文在翻译过程中发现很多地方直译成中文比较晦涩,所以采用了意译的方式,这不可避免的造成有一些地方可能和原文有一定的出入,所以如果你阅读的时候发现有任何的错误都可以给我发邮件:xyl.。
最后可以关注我微博大家一起沟通交流学习。微博地址:
最后希望这篇文档能帮上那些感觉看官方英文文档困难的人。如果可以我还是推荐尽量查看英文原文档,毕竟那是原汁原味,翻译总无法避免有一定的疏漏。
因为翻译的时候写在word里面,格式太多,图片上传麻烦,所以干脆直接转换为PDF提供给大家下载。以下提供目录预览:
INSTRUMENTS用户指南介绍
本文档组织结构
INSTRUMENTS快速入门
启动INSTRUMENTS
创建一个跟踪文档
浏览跟踪文档窗口
示例:快速使用一个跟踪
下一步是什么?
添加和配置INSTRUMENTS工具
使用INSTRUMENT库
修改库试图模式
查找库里面的某个instrument工具
新建一个自定义的instrument分组
添加和删除INSTRUMENTS工具
配置一个INSTRUMENT工具
记录跟踪数据
选择需要跟踪的进程
跟踪所有进程
跟踪一个已有的进程
跟踪一个新的进程
给每个Instrument工具指定不同的目标
使用快速启动键启动INSTRUMENTS
以最小模式运行
从XCODE运行INSTRUMENTS应用
无线连接IOS设备
记录用户界面轨迹
记录用户界面轨迹
重复记录用户界面轨迹
回放用户界面轨迹
查看和分析跟踪数据
查看数据的工具
扩展详细面板
运行浏览器
使用Sampler Instrument分析数据
使用Allocati***** Instrument工具分析数据
查找内存泄露
分析Core Data应用程序
保存和导入跟踪数据
保存跟踪文档
导出跟踪数据
从SAMPLE工具中导入数据
使用DTRACE数据
使用DTRACE创建自定义INSTRUMENTS工具
关于自定义INSTRUMENTS工具
创建自定义的INSTRUMENT工具
添加和删除探针
指定探针的提供者
给探针添加断言
给探针添加动作
编写自定义脚本的提示
编写BEGIN和END脚本
从自定义脚本里面访问内核数据
变量作用域
查找脚本错误
导出DTRACE脚本
内置INSTRUMENTS工具
CORE DATA INSTRUMENTS[CORE DATA相关]
Core Data Saves
Core Data Fetches
Core Data Faults
Core Data Cache Misses
DISPATCH INSTRUMENTS[并发相关]
ENERGY DIAGNOSITICS INSTRUMENTS[电池诊断相关]
电量使用(Energy Usage)
CPU 活动(CPU Acitivity)
显示亮度(Display Brightness)
休眠/唤醒(Sleep/Wake)
蓝牙(Bluetooth)
无线(WiFi)
定位(GPS)
FILE SYSTEM INSTRUMENTS[文件系统相关]
I/O 活动(I/O Activity)
文件锁(File Locks)
文件属性(File Attributes)
文件活动(File Activity)
目录I/O(Directory I/O)
GARBAGE COLLECTION INSTRUMENTS[垃圾回收相关]
垃圾回收(Garbage Collection)
GRAPHICS INSTRUMENTS[绘图相关]
核心动画(Core Animation)
OpenGL驱动器(OpenGL Driver)
OpenGL ES驱动器(OpenGL ES Driver)
OpenGL ES分析器(OpenGL ES Analyzer)
INPUT/OUTPUT INSTRUMENTS[输入输出相关]
读/写(Reads/Wirtes)
MASTER TRACKS INSTRUMENTS[界面操作跟踪相关]
用户界面(User Interface)
MEMORY INSTRUMENTS[内存相关]
共享内存(Shared Memory)
分配内存(Allocati*****)
内存泄露(Leaks)
SYSTEM INSTRUMENTS[系统相关]
时间分析器(Time Profiler)
旋转监控器(Spin Monitor)
取样(Sampler)
进程(Process)
网络活动监控器(Network Activity Monitor)
内存监控器(Memory Monitor)
硬盘监控器(Disk Monitor)
CPU监控器(CPU Monitor)
活动监控器(Activity Monitor)
THREADS/LOCKS INSTRUMENTS[线程相关]
Java线程(Java Thread)
UI AUTOMATION[界面自动化相关]
使用Automation Instrument工具
访问和操作用户界面元素
添加灵活的超时间
验证测试结果
输出测试结果和数据的日子
检测和指定设备的方向
测试多任务
USER INTERFACE INSTRUMENTS[用户界面相关]
Cocoa事件(Cocoa Events)
Carbon事件(Carbon Events)
&&&&推荐文章:
【上篇】【下篇】iOS性能优化:Instruments使用实战
招聘信息:
最近采用Instruments 来分析整个应用程序的性能.发现很多有意思的点,以及性能优化和一些分析性能消耗的技巧,小结如下。Instruments使用技巧关于Instruments官方有一个很有用的,当然如果不习惯官方英文可以在这里找到.Instruments 确实是一个很强大的工具,用它来收集关于一个或多个系统进程的性能和行为的数据极为方便,并能及时跟踪随着时间产生的数据.还可以广泛收集不同类型的数据.关于Instrument工具基本使用不在赘述.如下重点说明一些使用技巧.1.概览工具通过Xcode工具栏中Product->Profile可以启动,启动后界面如下:Instrument概览[via by chenkai]当点击Time Profiler应用程序开始运行后.就能获取到整个应用程序运行消耗时间分布和百分比.为了保证数据分析在统一使用场景真实行有如下点需要注意:在开始进行应用程序性能分析的时候,一定要使用真机,模拟器运行在Mac上,然而Mac上的CPU往往比iOS设备要快。相反,Mac上的GPU和iOS设备的完全不一样,模拟器不得已要在软件层面(CPU)模拟设备的GPU,这意味着GPU相关的操作在模拟器上运行的更慢,尤其是使用CAEAGLLayer来写一些OpenGL的代码时候. 这就导致模拟器性能数据和用户真机使用性能数据相去甚运.另外在开始性能分析前另外一件重要的事情是,应用程序运行一定要发布配置 而不是调试配置.在发布环境打包的时候,编译器会引入一系列提高性能的优化,例如去掉调试符号或者移除并重新组织代码.另iOS引入一种"Watch Dog"[看门狗]机制.不同的场景下,“看门狗”会监测应用的性能。如果超出了该场景所规定的运行时间,“看门狗”就会强制终结这个应用的进程.开发者可以crashlog看到对应的日志.但Xcode在调试配置下会禁用"Watch Dog".2.Time Profiler选择Time Profiler启动.time profile时间分析工具用来检测应用CPU的使用情况.可以看到应用程序中各个方法正在消耗CPU时间.使用大量CPU不一定是个问题.类似我们客户端中不同场景的天气动画[类似大雨]的路径就对CPU依赖就非常高,动画本身也是非常苛刻且耗费资源较多的任务.点击Record 开始运行.Time Profile 分析界面[via by chenkai]刚开始我们拿到分析数据时往往是这样的:性能数据[via by chenkai]这里显示的是执行代码完整路径,其中系统和应用本身一些调用路径完全揉捏在一起.完全看不到我们关心的应用程序中实际代码执行耗时和代码路径实际所在位置.简单的方式可以快速勾选右边Call Tree中Separate Thread和Hide System Libraries两个选项[后面会解释选项作用]:拆分后性能数据[via by chenkai]可以看到直接能够看到应用程序各个方法调用耗时直接路径,剔除掉了系统相关方法和反向调用树路径.清爽很多.如果觉得这还不够直观,选择任意一个耗时方法分支[这里选择WeatherViewController viewDidLoad]双击进入会看到:代码&耗时详情可以直接定位到viewDidLoad的代码,也可以直观的看到改方法下调用其他方法耗时的时间.类似[self loadCityWeatherScroollerView]耗时是121x,x既耗时单位这里为ms毫秒.当然如果直接在Instrument找到问题觉得不方便修改,可以直接点击右上方Xcode按钮会直接定位Xcode对应调用方法入口.这样很容易能够快速定位代码占用CPU最多的方法.也可以打开Xcode快速修改并重新运行Profile来看修改后耗时前后对比.简单便捷.这里对右侧call tree选项有必要做一下说明[官方user guide翻译]:Separate By Thread:线程分离,只有这样才能在调用路径中能够清晰看到占用CPU最大的线程.Invert Call Tree:从上到下跟踪堆栈信息.这个选项可以快捷的看到方法调用路径最深方法占用CPU耗时,比如FuncA{FunB{FunC}},勾选后堆栈以C->B->A把调用层级最深的C显示最外面.&Hide Missing Symbols:如果dSYM无法找到你的APP或者调用系统框架的话,那么表中将看到调用方法名只能看到16进制的数值,勾选这个选项则可以隐藏这些符号,便于简化分析数据.Hide System Libraries:这个就更有用了,勾选后耗时调用路径只会显示app耗时的代码,性能分析普遍我们都比较关系自己代码的耗时而不是系统的.基本是必选项.注意有些代码耗时也会纳入系统层级,可以进行勾选前后前后对执行路径进行比对会非常有用.关于其他方法不再赘述.性能分析&代码优化我们这次性能优化主要针对如下两个使用场景:A:应用程序第一次启动到进入天气首页的时间.B:从后台切到前台天气首页占用时间.在还没有拿到性能分析数据之前,一直认为第一次启动耗时主要浪费AppDelegate中第三方框架初始化上[类似WeiBo&WeChat 相关SDK初始化调用].当我们拿到实际性能数据耗时占用比时发现实际情况并非如此:启动耗时如上可以看到应用程序启动初始化工作主要会在MJAppDelegate如下两个方法展开:willFinishLaunchingWithOptions和didFinishLaunchingWithOptions,其中第三方框架初始化工作主要是willFinishLaunchingWithOptions中完成的.而实际情况耗时占比非常小.基本可以忽略不计.而我们要优化两个启动时间场景,不同在于.第一次进入应用需要经过新手教程、添加城市、请求城市数据、解析数据、初始化天气首页UI元素并加载场景动画. 而从后台进入时则从本地存储DT文件中解析天气数据、初始化天气首页UI元素并加载天气动画.1.NSDateFormatter问题凸显针对这点重点分析应用启动&天气首页耗时. 在AB两个场景均发现加载首页元素发现如下问题:NSDate(TimeAgo)getDateStrByTimeZone耗时继续跟踪发现:NSDate耗时在AB两个场景里均出现加载MJLineChartView 和 TendencyChartView 时获取时区对应时间上耗时较大.而耗时主要在getDateStrByTimeZone这个方法调用上.getDateStrByTimeZone方法其中创建一个NSDateFormatter对象平均耗时33ms左右 而设置NSDateFormatter的3个属性平均耗时也在30ms左右.因为首页24小时天气和未来几天预报中.需要for循环中遍历数据,导致这个方法别重复调用多次,则消耗时间不断叠加.针对这个问题:NSDateFormatter对象本身初始化很慢,同样还有NSCalendar也是如此.然而在一些使用场景中不可避免要使用他们,比如Json数据解析中.使用这个对象同时避免其性能开销带来性能开销,一般比较好的方式是通过添加属性(推荐)或创建静态变量保持该对象只被初始化一次,而被多次复用.不得不值得一提的是设置一个NSDateFormatter属性速度差不多是和创建新的实例对象一样慢!添加属性方式如下:属性方式针对NSDateFormatter时间开销出了重用对象外,尽量避免采用其处理多个日期格式.当然针对日期格式处理如果需要提高更多速度,可以直接采用C,可以采用来规避这个问题..2.UIImage缓存取舍在项目代码中看到大量使用如下代码:UIImage使用在Main Thread中发现不同动画场景中Image IO 开销和耗时所占比例均不一,在UIImage元素较多总体叠加耗时也会占用一定比例.内存开销也会明显增高.UIImage加载图片方式一般有两种:A:imagedNamed初始化B:imageWithContentsOfFile初始化二者不同之处在于,imageNamed默认加载图片成功后会内存中缓存图片,这个方法用一个指定的名字在系统缓存中查找并返回一个图片对象.如果缓存中没有找到相应的图片对象,则从指定地方加载图片然后缓存对象,并返回这个图片对象.而imageWithContentsOfFile则仅只加载图片,不缓存.大量使用imageNamed方式会在不需要缓存的地方额外增加开销CPU的时间来做这件事.当应用程序需要加载一张比较大的图片并且使用一次性,那么其实是没有必要去缓存这个图片的,用imageWithContentsOfFile是最为经济的方式,这样不会因为UIImage元素较多情况下,CPU会被逐个分散在不必要缓存上浪费过多时间.使用场景需要编程时,应该根据实际应用场景加以区分,UIimage虽小,但使用元素较多问题会有所凸显.3.天气首页加载策略在AB两种场景把性能数据对比分析发现:天气首页WeatherView更新耗时天气首页WeatherView初始化耗时一直300ms-450ms之间,占据首页耗时很大一部分.且一直固定的开销.占据Main Thread3分之一.而用户进入最先看到是天气首页上半部分:上半部分而下半部分需要滚动才能看到下半部分.且不一定触发:下半部分而现在整个首页View的初始化和更新全部放到主线程来做.其中WeatherInfoView updateAllInfo方法更新耗时最长.更多的view意味着更多的渲染,也就是意味更多的CPU和内存消耗,对于我们天气首页在UIScrollView里边嵌套了很多view更是如此。而针对这种情况不要在主线程承载过多的操作.uikit渲染,用户输入回应都需要主进程上完成.主线程被意外block或者加载响应耗时过多都会影响到用户体验.而针对资源消耗过大操作,处理原则是最小化主线程的CPU占用,将工作“搬离”主线程, 不要阻塞主线程.类似本地一些IO完全移到其他线程来做.调试time profiler过程中发现,即使占用了很少的CPU时间(如果你在Time Profiler中看到这些的数据),也可能会阻塞主线程。磁盘、网络、Lock、dispatch_sync以及向其它进程/线程发送消息都会阻塞主线 程。Time Profiler只能检测出占用CPU过多的堆栈,但检测不了这些IO的问题.很奇怪.在System Trace里面突然发现了CPU Time很低,但Wait Time很高的调用,说明在主线程处理I/O已经严重损害了app的性能,这个时候考虑把这个操作优化了.而针对我们应用首页ui中多个view,在加载策略完全可以采用多线程进行同步加载,只把上半部分放在主线程中加载,下班可以同时开一个线程进行同步加载.这样可以大大降低组线程初始化和更新时间,当首页初始化完毕已经呈现是,下半部分其实已经另外一个线程处理完毕.另外针对单个view 尽量不要在viewWillAppear费时的操作,viewWillAppear在 view 显示之前被调用,出于效率考虑,在这个方法中不要处理复杂费时的事情;只应该在这个方法设置 view 的显示属性之类的简单事情,比如背景色,字体等。不然,用户会明显感觉到 view 显示迟钝.4:应用首次加载时间应用首次启动加载操作:首次加载首次加载坐了如下操作:A: 链接和载入:可以在Time Profile中显示dyld载入库函数,库会被映射到地址空间,同时完成绑定以及静态初始化.B: UIKit初始化:如果应用的Root View Controller是由XIB实现的,也会在启动时被初始化.C: 应用回调:调用UIApplicationDeleagte的回调:application:didFinishLaunchingWithOptions.D: 第一次Core Animation调用:在启动后的方法-[UIApplication _resportAppLaunchFinished]中调用CA::Transaction::commit实现第一帧画面的绘制.应用程序首次加载中启动方法willFinishLaunchingWithOptions和didFinishLaunchingWithOptions只做应用程序首次启动必须的要操作,而针对_dyid_start在初始化库framework函数的操作.不必要的Framework不要链接,避免首次加载耗时.小结如上.很多地方代码调用和底层机制看的不是特别明白,整理总结关于优化部分实在有限,如上仅供各位参考.另外Instruments确实是把分析代码利器.目前没有任何一个第三方工具可以去替代.推荐各位使用.作者:chenkai()
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量3187点击量3024点击量2959点击量2917点击量2670点击量2601点击量2511点击量2338点击量2323
&2016 Chukong Technologies,Inc.
京公网安备89

我要回帖

更多关于 linux查看进程消耗 的文章

 

随机推荐