2012年11月29日
1
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
介绍移动平台的开发是严格与内存管理有关。虽然技术进步使移动设备的存储容量达到低端台式机的水平,由开发商提出的申请也增长比例。在设备的屏幕尺寸主要问题在于 - 更高的对角线意味着更高的图形分辨率的使用和更高的内存需求。开发人员所熟悉的Android平台也知道垃圾回收器不会完全保护应用程序的内存泄漏。这是不难想象对性能的影响,如果发生这种情况,一个大的,动态的应用程序。这就是为什么优秀的内存分析技能是开发必不可少的。本文将介绍一些有用的工具和实践来检测关键的内存泄漏。 工具有许多工具可以帮助识别内存泄漏。下面是一个示例列表,这些工具的简要说明:
命名法内存分析必然会有许多专用术语。由于这些将会经常在这篇文章中出现,这里是他们的定义:
内存泄漏有泄漏时,有一个参照是防止该对象被垃圾收集未使用的对象。一个对象可以有一个参照束的其他对象和单个基准可以防止GC进行大量的对象。 浅堆内存由一个对象所消耗。 例如:
myObject的总浅大小为20字节。 保留堆的,可以通过释放一个对象被释放的对象总大小。 GC之前:GC将运行在对象A。 GC后:释放对象A(300字节),将导致释放对象B(50字节)和C(50字节)。还通过释放对象B,对象D(100字节)将被垃圾收集。因此,通过收集对象A - GC将能够释放500字节(浅堆的总和),这是保留的堆。 检测内存泄漏有几种方法来发现内存泄漏。本节将介绍这样做的几种方法。 logcat的消息发现一个内存泄漏的第一种方法是检查logcat的输出。当垃圾收集器开始工作它把消息转换为logcat的输出。它看起来应该像这样: D / dalvikm(14302):GC_CONCURRENT释放2349K,65%无3246K/9551K,外部4703K/5261K,暂停2ms的2毫秒 消息的第一部分表示GC的类型(原因GC的)。有四种不同类型:
“解放2349K,” - 表示内存是多大的释放。 “65%自由3246K/9551K” - 表示的剩余空间百分比,活动对象的大小和总堆大小。 “外部4703K/5261K” - 表示外部内存分配,有多少外部存储器的应用程序已分配和分配的软限制。 “顿了顿2ms的2毫秒” - 表示多少时间花费在GC完成收集。 有了这些信息,可以经过几集,告诉如果GC被成功释放内存。如果分配的内存不会一段时间之后下去(而且还在不断增加),很显然是有内存泄漏。这里是一个相当微不足道泄漏的例子: 的OutOfMemoryError异常当可用资源耗尽了内存不足错误异常。这可能表明存在内存泄漏。此方法不知道是否有因为当开发人员尝试分配大量的内存(如位图),总堆大小将超过该平台的限制,可能会出现异常泄漏的最佳方式。它肯定表示,开发商应该反思一下自己的内存管理方法。 跟踪内存分配在成功诊断内存问题现在是时候寻找病因是什么。有两个工具将在分析应用程序的帮助。 DDMSDDMS是一个强大的工具,可以提供有价值的信息,并用于MAT一个HPROF内存转储文件。从Eclipse IDE中选择窗口 - >打开透视图 - > DDMS DDMS访问。 该接口是相当多的自我解释所以没有必要进入描述它的细节,但有两个选项卡,可能会有所帮助。其中第一个就是“分配跟踪器”显示当前分配,如分配的班级,分配大小和分配的内存位置的详细统计信息。 第二个被称为“堆”。大约有多少每个类的对象被分配在内存和详细信息的实际大小。 倾销HPROF文件很容易。连接设备,运行应用程序,使用了一段时间,然后点击“转储HPROF文件”按钮位于工具栏的设备选项卡上。如果已经安装了MAT插件,Eclipse会自动打开该文件。 内存分析工具(MAT)MAT是一个非常强大的内存分析工具。它可以作为一个独立的版本,或作为一个Eclipse插件。这两者之间唯一的区别是它们如何处理加载HPROF文件。单机版需要一个转换的HPROF文件。这可以通过使用附带的Android SDK(定位于工具文件夹中)的HPROF-CONV程序来实现。在使用该插件的版本就没有必要将文件转换。要打开MAT选择窗口 - >打开透视图 - >其他 - >内存分析。 概观加载HPROF文件后,用户呈现总览屏幕。它包括以下元素:
直方图其中最有用的工具是MAT。它可以列出任何给定类的实例的数量。当寻找一个内存泄漏或内存问题,这是好事,先来看一下最“濒危”类和检查多少实例存在。还有一个非常有用的正则表达式的字段,以允许搜索特定类别。直方图视图还允许在计算最小和精确保留堆选定对象。
直方图的另一部分是项目的上下文菜单。它允许访问查询(在概述部分所述),将所选对象被调用。这是一个非常重要的工具,当涉及到诊断内存问题。如果有可疑的内存泄漏,开发人员可以检查的具体项目的传入引用,找出哪些是保持被收集。当然,垫还允许过滤从这样的查询(例如,通过限制对象到指定的类)的结果。接收对象的列表从查询后,开发商都带有另外一个有用的工具 - “路径与GC根”。它包含了像不包括弱引用许多过滤选项 - 这是最常见的过滤器之一,因为弱引用不会阻止对象被收集由GC没有必要一一列举。如果用户不满意的预定义查询太让他从头开始定制或编写一个如此有可能性无限多。详情和查询的实际应用将在后面介绍这篇文章。 支配树支配树是在MAT第二个有用的观点。它列出了保留的堆量有组织的系统对象的实例。它可以从概述,或从指定的对象类的上下文菜单中选择“立即统治者”选项进行访问。前者显示从内存转储,后者的发现和聚合所有支配一组给定的类级对象的对象的所有对象。以下是支配树的一些重要性质:
这三个属性是支配树视图的很好的理解非常重要。使用该视图熟练开发人员可以迅速地找到并引用它们是不必要的每个对象的留存堆。 查询查询是旨在帮助通过对象的森林得到的基本工具。内存分析发现在许多对象的堆栈不需要引用的过程 - 它不是一件容易的事。过滤这些对象和引用使得它更容易。有迹象表明,开发商应该学会成功调试内存中的两个关键技能。第一是自己的应用程序的知识。有没有办法找到一个内存问题,如果找它的人并不知道应用程序的对象结构应该是什么样子。第二个技巧是使用筛选器和查询。如果开发人员知道对象的结构并且知道如何获得理想的观点就很容易发现异常。这里是在MAT内置的查询的列表:
它是不是在项目上下文菜单中可作为其中的一些较早已经描述查询的完整列表。有关可用查询更多信息,请参阅本文中的“延伸阅读”部分。 报告内存分析工具有一个内置的报告系统,自动分析内存转储和生成报告的用户。报告的第一种类型是“泄漏疑点”的报告。MAT分析内存转储,并检查是否有可能维持生命的一些参考任何大的物体。请记住,这并不意味着泄漏嫌疑人实际上是真正的泄漏。第二个是“顶部组件”的报告 - 它包括对可能的内存浪费,最大的对象和有关引用的一些统计信息。这是一个伟大的地方去寻找内存优化。 泄漏疑点报告该泄漏疑点报告包括有关潜在的泄漏问题的信息。重要的是要记住,这些列出的对象不必是实际的泄漏是非常重要的。它仍然是一个非常翔实的报告和一个伟大的地方开始寻找泄漏。该报告的主要组成部分是描述和最短路径,以聚点。第三个组成部分(由类累计对象),距离第二个(只是排序的类)派生。 顶部组件报告这是一个非常翔实的报告,特别是对那些谁正在寻找一种方法来减少内存使用。它由内存废弃物嫌疑人和有关引用的一些数据。整份报告都是自我解释所以没有必要详细描述它。 使用MAT检测内存泄漏本节将介绍在发生内存泄漏的实际使用内存分析工具。就本节而言用一备的内存泄漏一些示例代码是必要的。 与内存泄漏示例代码此示例代码写入到显示内存泄漏的主要原因,并提供与MAT工作的材料。有两种最常见的内存泄漏类型。第一种是静态引用非静态内部类,这将保持一个参考的活动,并防止它被GC。当每一个屏幕方向变化的onCreate方法会被调用,一个新的MainActivity实例将被创建。由于旧的引用,旧的活动将不会被垃圾收集。第二种情况被称为“上下文泄漏”。这种情况下是很难发现传递给一个静态类,它保持它作为一个领域应用程序的上下文中的主要问题在于 - 这当然是一个硬引用。 第一个内存泄漏演示这是第一个内存泄露的源代码,将让MainActivity被释放的GC。当然对象和类名被选择为使得它更容易表现出泄漏。在现实生活中的情况就不会那么容易。
首先来看看从这段代码输出的LogCat中。连接的设备,运行logcat中,启动应用程序并旋转装置几次。下面是示例输出的LogCat中,如图17: 第一次旋转后的差大约是3MB。再过旋转堆的大小由3MB再次增长。它是第一个警告的东西是不正确的。也LogCat中不显示释放分配的内存的任何迹象。现在是时候来看看什么是应用程序的内存里面怎么回事。运行DDMS工具,得到HPROF文件并打开它的内存分析工具里面。 主屏幕是有一些导致泄漏 - 它看起来应该与此类似图18所示: 饼图显示,显著数额整个内存的保留资源 - 这是一个具有图形用户界面的任何应用程序的正常行为,但此示例代码里面只有一个资源,以便该问题可能躺在那里。还有两个FrameLayout中的实例(每个3MB)需要调查。有迹象表明,开发商可以按照来检测内存泄漏的几个路径。 基于直方图的搜索让我们来看看直方图视图。还有就是内存的字节数和Bitmap类分配了显著金额。来看看一个Bitmap类对象的引用传入。这份名单应该是这样的,如图19所示: 有三个大位图在内存中。该示例应用程序应该有最差的一个或两个内存泄漏。的内存量适合LogCat中输出。让我们来检查路径对GC根(不包括所有类型的弱引用)的每个实例。第一个位图对象,似乎它有一个引用本身,所以它不会被任何外部参考举行,并等待垃圾回收器来照顾它要被罚款。 第二个有一点点更多的参考资料,但一切都显得平凡。看来,这个对象是一个积极的(可见的)位图 - 这就是为什么它引用了该活动,背景,窗口管理等。 第三位是一个打击。它只有一个硬盘的参考路径,GC根,它是由“leakInstance”对象中。这表明,“leakInstance”对象,防止收集的位图。 也有MainActivity的道路上 - 这并不奇怪,看着大家都知道,每一个屏幕旋转(这将创建一个新的Activity)有泄漏。让我们来看看发生了什么。首先使用正则表达式过滤器来查找直方图视图中MainActivity对象。 第一个警告是,有MainActivity的三个实例。当然,这并不一定是一个漏,因为有时活动保持活力长于其他对象,但让我们来看看如果有什么会阻止他们得到的GC。要做到这一点,列出所有与传入的引用的对象。作为直方图显示有三个不同的实例。现在是时候检查路径到GC根。 又是第一个MainActivity对象都有一个引用上下文,ActivityThread所以看起来它是目前活跃的活动。 第二个对象再次拥有一个引用本身,所以它正等待被垃圾收集。一切都很好了这一点。 基于支配树搜索有很多方法,开发人员可以找到内存泄漏。本文将仅显示其中的几个。第二个将基于一个支配树视图。打开HPROF转储的支配树视图。由保留堆大小排序的列表的内容。正如预测的那样在列表顶部的资源是那些具有最大保留的堆,但也存在的FrameLayout类(3MB每个)三个实例和一个Bitmap类的实例(1MB)。该FrameLayout中物体看起来很可疑,所以让我们来仔细看看它们。由于支配树已经显示出真正的对象,所以它是可以显示的路径,GC根不先列出参考文献。 第一项实际上是一个打击!对GC根的唯一途径是通过“leakInstance”对象,这样有泄漏。 第二个和第三个对象是当前帧版式视图中,一个正在等待得到收集。 让我们来看看位图对象,因为它也可能导致泄漏。选择android.graphics.Bitmap并显示其路径GC根所不包括的所有弱引用。 有位图类型的三个对象,以便为他们每个人寻找到GC根的路径。 这种情况重演 - 其中的两个实例都清楚(他们有引用到系统类),但第三个实例指向“leakInstance”,所以它是维持生命这个参考。 大概有几百得到实际泄漏的方法。它是由开发人员来选择哪条路,他应该采取以及如何分析内存。 第二个内存泄漏演示第二个内存泄漏的情况,包括应用程序上下文。它被作为参数传递到另一个单独的类,并在现场保存在那里。整个过程将继续MainActivity被收集的垃圾收集器。相同的结果可以通过保持上下文静态字段应该避免使这种做法来实现。为了避免重复,只有一个方法查找泄漏的方式进行说明。 这里是代码:
运行应用程序,倾倒HPROF文件和MAT运行它之后与此类似的一个概览屏幕上将会出现。 概览屏幕并没有告诉任何重要的,所以开发人员必须继续他的搜索。在这个片段中有没有位图或许多资源,但直方图视图显示,有MainActivity的许多实例 - 检查他们可能会提供一些进一步的信息。 该装置旋转3次,直方图显示,有MainActivity的四个实例。现在是时候检查是否有从垃圾收集使他们的任何对象。要做到这一点,列出与传入的引用的对象。只是通过扩展一审认为有一个暗示,这个对象是当前活动(它包含对ActivityThread)。 继续搜索,并列出路径GC根有一些包含对自己和一个实例指向mInstance内SingletonClass和参考到目前的活动(从mSingletonClass)的两个实例。即泄漏。 它是清晰可见的垃圾收集,防止由上下文。还有另外一个问题 - 创造活动的另一个实例后传递给SingletonClass的背景下保持不变。这是一个关键问题。上下文引用指向一个活动,不再是必要的,但保持正因为如此活着。这样的问题可能是应用非常关键的,可以创建不必要的行为,而且很难被发现在更大的项目。 补充阅读内存分析是一个广泛的话题,不能在很短的文章呈现的细节。有专门用于内存管理的许多书籍,文章和演讲。下面是一个简短的清单,读者可以用它来进一步陷入这个话题。 |
MAT 工具使用详解
最新推荐文章于 2024-08-25 23:40:52 发布