(该文翻译者:Edison Guo 原文见Flex Builder 3.x 帮助文档 About Profiling)
Adobe Flex 性能分析工具(Profiler)能够帮助我们诊断应用程序中的性能瓶颈和内存泄漏。我们从Flex Builder中启动性能分析工具之后,在我们同应用程序的交互过程中,性能分析工具将记录应用程序的各种状态。例如,对象的数量及这些对象的大小,被调用的方法的数量以及调用这些方法所消耗的时间。
分析应用程序能够帮助我们确定以下问题:
n 调用频率 有时,我们会多次调用一些计算代价昂贵(耗时)的方法,而这些调用是不必要的。通过识别那些经常被调用的方法,我们能够在调节性能的过程中,专注于应用程序中对性能影响最大的地方。
n 方法耗时 性能分析工具能够告诉我们一个调用特定方法所消耗的时间。如果这个方法被调用了多次,性能分析工具将告诉我们,在与应用程序交互的这段时间里,调用这个方法所消耗的平均时间。如果其中的一些方法造成了性能瓶颈,我们可以想办法优化一下它们。
n 调用堆栈 通过追踪某一方法的调用堆栈,我们可以看到应用程序调用该方法的完整路径。
n 实例数量(对象分配) 有时,我们会发现同一对象被创建了太多次,而我们只需要这一对象的几个实例。在这些情况下,如果只需要一个实例,我们可能考虑使用单件模式;如果需要多个,则应用其他技术来减少对象分配。如果确实需要很多该对象的实例,我们得考虑优化对象本身来降低资源总数以及内存占用量。
n 对象大小 如果观察到某些对象大小异常,我们可以尝试优化它们以减少其内存占用量。程序中某些对象被多次创建时尤为有效。
n 垃圾回收 比较性能快照时,我们可能发现一些不再被程序使用的对象仍然在“loitering”,或者存储在内存中。为了避免内存泄漏,我们可以添加一些逻辑,来移除这些对象身上的“残余”引用。
我们不应当把性能分析看成一个与应用程序开发毫无关联,相对独立的阶段。相反,性能分析应当或多或少的集成到整个开发过程的每一阶段。我们在开发过程中应尽可能的早进行性能分析,多进行性能分析。这样,我们才能更快的找出有问题的地方。性能分析是一个反复进行的过程,尽可能频繁的进行性能分析将使我们受益无穷。
(原文见Using the Flex Builder 3.x Profiler)
最近接触了许多内存泄漏方面的问题。 现在我终于有时间将我在分析内存泄漏中用到的技术写下来了。
我创建了一个SWF文件来代替 PowerPoint。这样,在看报告的同时,大家也可以学习如何使用Profiler。在文章中,我说明了性能分析工具中显示的内存与通过System.totalMemory获取的内存及进程(Flash Player, IE, Firefox)所占用内存的不同,并演示了针对内存泄漏问题中常见的两个情景,该如何分析。
查看报告(英文)
和往常一样,以下是附加的说明。
经常听到的一个话题是如何实现XML驱动或数据驱动的用户界面。在这个SWF文件中,我演示了一种实现方法。报告内容由一个XML 文件控制。一个独立的引擎解析这个XML文件,根据解析的结果创建特定的交互部件。改变报告的内容仅需要修改XML文件即可。我也可以轻松的添加新的部件。源码可以从这里得到:
这个SWF同时也演示了一种改进启动时间的技术。我们的Blog系统用起来太痛苦了,我不希望在发表日志的时候上传两个以上的文件,而这篇报告有成打的图片需要上传。因此,我将这些图片嵌入到SWF中,而不是从外部将它们加载进来。但是,下载这些图片所花费的时间将延迟启动。为了避免这种情况,我将所有的图片塞进SWF的第三帧,这样Flex就能迅速启动并运行,而这些图片在SWF文件末尾才会被下载。之所以这样做,是因为这些图片并会立刻被使用。
希望这些能帮助大家!
译者注:
如何将图片嵌入到第三帧?
//将第三帧加载图片以改善加载时间。对于MXMLC,添加:
// –frame=Three,Frame3Assets
//作为编译参数
// 能够这样做是因为我们首次呈现给用户的内容中不包含这些图片
(原文见profiler scenarios)
l 如何使用Flex Builder 3.x性能分析工具找出内存泄漏依赖于应用程序自身的行为
l 有两种常见情景
n 创建/销毁
n 替换当前
l 在一个创建/销毁情景中,一连串的对象被创建,之后被销毁
n 弹出式对话框是一个经典的例子
l 所有被创建的对象所占用的内存都应被回收
l 内存占用量在开始的时候有一个初始值,对象被创建后,该值增长,对象被删除,内存被回收后,该值应恢复为初始值
l 在一个“替换当前”的情景中,一连串的对象被类似的一系列新对象所替换
n 这个幻灯片就是一个例子
n 当改变幻灯片的时候,老的幻灯片被新的所替换
l 因为对象被替换而不是销毁,内存占用量应大致保持在相同的水平
l 性能分析工具被设计出来主要用于诊断创建/销毁情景
n “游荡对象”视图显示出存在于最近一次内存快照中,之前的内存快照中没有出现过的对象
n 在“替换当前”情景中“游荡对象”将显示所有(或大多数)对象,因为替换的对象总是在之前的内存快照中不存在的对象
l 十分不幸,这意味着分析“替换当前”场景时,我们不得不手动的比较内存快照
译者注:
在创建/销毁场景中,我们可以很简单的通过“游荡对象”视图,找到那些应当被销毁,但是依然存在的对象。因为,只要是出现在视图中的,都是被怀疑的对象。浏览并锁定那些应当被销毁的对象即可。
在“替换当前”情景中,因为“游荡对象”显示出了所有的对象,即使它不应当被销毁。因此,我们没办法简单的判断一个对象是否该存在,只能手动的在内存快照中寻找答案。
游荡对象(Loitering Objects):应当被销毁,释放掉其所占用的内存,但是因为某种原因仍在内存中保留的对象。
l 这一SWF文件能够用来演示如何分析外部应用程序的性能
n 通常情况下,我们会打开一个项目,并分析这个项目中的应用程序
n 我们一起来看看如何分析“创建/销毁”和“替换当前”情景
l 开始之前,打开“Profiling”视图,在“Profile”菜单下选择“Profile External Application”
l 我们会看到一个如下的对话框
l 点击“New”按钮来添加一个新的应用程序
l 我们将看到一个如下的对话框
l 输入如图所示的URL,然后点击“OK”
l 应该有一个这样的对话框出现
l 点击这一SWF文件的URL使它高亮
l 点击“Launch”按钮
l “Configure Profiler”对话框出现
l 取消对“Enable performance profiling”的选择,选择“Generate object allocation stack traces”
l 点击“Resume”按钮
l 几秒钟后,显示器的屏幕看起来应该是这样的:
l “Live Objects”视图显示出到目前为止,在应用程序执行过程中,已经被实例化了的一些类
n 实际上这个视图能显示出所有被实例化的类,但是默认情况下,许多类被过滤掉了
l 右侧的四列数据有如下的含义和用途:
n “Cumulative Instances”,到目前为止,该类被创建过的实例总数。这并不是当前存在的实例数目。这一数据很难被用到,除非要考虑回收实例是否比创建新的更好的时候
n “Instances”,当前存在的属于该类的实例数目。如果为0,则表明所有的实例都被作为垃圾回收了
n “Cumulative Memory”,“Cumulative Instances”所占用的内存总量
n “Memory”,“Instances”所占用的内存总量
l 内存占用量可能会误导我们。它包含的仅仅是为了保留该类中定义的所有属性,所分配的字节数(每一属性大概需要4字节)。它并不包含:
n 子对象或字符串占用的内存。这在它们自己的行里
n 播放器追踪对象所需的内存。每一对象有许多内存控制块没有被计算进来,性能分析工具中也没能显示出所有“display objects”背后的隐藏对象所占用的内存
n 或者用于处理网络通信或文件访问的内存
n 浏览器占用的内存
n 存储Flash Player编码及预定义变量所需的内存
n JIT缓存占用的内存
l 当前的内存占用量与操作系统告诉我们的——通常是通过任务管理器得到的——应用程序所占用的内存完全不同。操作系统告诉我们的通常会远大于前者
l 另一种测量内存占用量的方法是System.totalMemory。通过检查这一数值,我们会发现该值在分析工具和操作系统两者所显示的内存占用量之间。
l Flex Player 以每4096字节为一个块,将内存从操作系统中划分给小的对象。比如,一个4096字节的块包含了256个16字节的对象,而另一个却包含了16个256字节的对象。
l flash.system.System.totalMemory测量从操作系统分配的块的数量以及其他的一些较大的内存分配。它不包括:
n 操作系统用来显示可视部分、获取事件、处理网络或文件I/O所需的内存
n 浏览器占用的内存
n 存储Flash Player编码及预定义变量所需的内存
n JIT缓存占用的内存
l 当Flash Player需要分配第257个16字节数据块的时候,它会从操作系统分配另一个4096字节的块,同时将其预留给16字节的对象
l 如果不再需要一个块中的所有16字节对象,Flash Player会将该块所占用的内存释放,返给操作系统
l 可能会出现这样的情景:分配了上千个16字节对象,但是每256个对象只释放其中的255个,这样的话最终会得到很多块,每个块只包含了很少的几个16字节对象
l Flash Player不会合并这些对象稀疏的块,这样System.totalMemory以及操作系统告诉我们的将远远大于分析工具显示的,因为大量的系统内存被分配出去却未的到充分的利用
l 让我们来分析一个创建/销毁场景中的内存泄漏问题。按下“Memory Snapshot”按钮
l 应用程序下面,一个内存快照入口将出现在“Profile”选项卡中
l 现在按下这个应用程序底部的“Show Leak Dialog”按钮
l 现在使用右上方的关闭按钮关闭该对话框
l 理论上,这个对话框应该已经被销毁了。让我们来找出它。
l 再次按下“Memory Snapshot”按钮。应用程序下面,第二个内存快照入口将出现在“Profile”选项卡中
l 点选一个内存快照入口,按住CTRL再点选第二个,接着按下“Loitering Objects”按钮
l “游荡对象”视图看起来应该是这样的
l 噢!看起来有很多东西泄漏了
l 尽快有很多东西泄漏了,但通常是由一到两个根本原因引起的
l 找到并排除这一两个根本原因,将阻止大多数的内存泄漏——如果不是所有对象都泄漏的话
l 我经常由“高层”对象开始,因为通常它们会包含许多来自子对象的引用,而释放这一高层对象,同时也能释放其子对象
l 足够确定的是,“LeakyDialog”是一个在“游荡对象”视图中有一个实例的高层对象,这意味着它没有被释放
l 我们需要检查一下谁引用了LeakyDialog。在“游荡对象”视图中双击“LeakyDialog”查看对它的所有引用
l 一共有31个引用。但并不是所有引用都有问题。大多数是“循环引用”
l 一个“循环引用”可能是“LeakyDialog”的某个子对象对其的一个引用
n 一个循环可以追溯到孙对象或者曾孙对象
l 让我们看看UITextField中的一个引用
l 我们展开UITextField的引用后,会立刻发现其引用对象的ID指向的是LeakyDialog的ID或者该UITextField的ID
l 一旦我们碰到一个ID与LeakyDialog的相同,或者与正在检查的引用的ID相同,我们不需要展开这个ID来查看它是否为内存泄漏的根源,因为这是一个“循环引用”
l 这个过程可能会花一些时间,但最终我们会找到内存泄漏的根源
l 有时,内存泄漏可能是一个直接到“LeakyDialog”的引用造成的,但有时也可能是由于引用其子对象而造成的
l 最终我们会展看并检查“[function]”引用,如下图所示
本文转自:http://blog.minidx.com/2008/12/21/1800.html