Android内存泄露测试不再蓝瘦,香菇

KristaBanck 7年前
   <p>在进行 Android 内存泄露分析时,面对成千上万个对象,你是否蓝瘦 , 香菇?作为测试人员你在进行内存泄露测试之后,是否有勇气告诉开发同事程序已经没有内存泄露,可以放心发布了?</p>    <p>众所周知,内存泄露测试难点在于准确的定位出泄露的对象。现在小哥有种方法通过一条命令就高效全面的得到 Android 程序内存泄露对象,让你不再蓝瘦,香菇!</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/bb7ebfd0a5899bc9ff3b85b181769f09.jpg"></p>    <h3><strong>1.Android内存泄露自动化分析方法</strong></h3>    <p>目前我知道的几种常用的 Android 内存泄漏方案主要有 MAT 、腾讯内部开发的 Finder 、 LeakCanary 以及浏览器目前使用的方法。</p>    <p>几种方法,我们通过以下的几个纬度进行对比。</p>    <table cellspacing="0">     <tbody>      <tr>       <td> </td>       <td><strong>MAT </strong></td>       <td><strong>Finder </strong></td>       <td><strong>LeakCanary </strong></td>       <td><strong>QB </strong> <strong>方案</strong></td>      </tr>      <tr>       <td><strong>手动 / 自动 </strong></td>       <td>手动</td>       <td>手动</td>       <td>自动</td>       <td>自动</td>      </tr>      <tr>       <td><strong>分析准确度</strong></td>       <td>低</td>       <td>高</td>       <td>高</td>       <td>高</td>      </tr>      <tr>       <td><strong>分析难度</strong></td>       <td>高</td>       <td>中</td>       <td>低</td>       <td>低</td>      </tr>      <tr>       <td><strong>分析效率</strong></td>       <td>低</td>       <td>中</td>       <td>高</td>       <td>高</td>      </tr>      <tr>       <td><strong>是否修改代码</strong></td>       <td>否</td>       <td>否</td>       <td>是</td>       <td>否</td>      </tr>      <tr>       <td><strong>应用场景</strong></td>       <td>所有</td>       <td>所有</td>       <td>部分</td>       <td>所有</td>      </tr>     </tbody>    </table>    <p>最原始的 Java 内存泄露分析方式,是通过 MAT 将测试操作前后的 Hprof 文件进行对比,然后根据对比结果来尝试识别出泄露对象,这种方法效率比较低。</p>    <p>如果使用腾讯的 Finder 工具来进行泄露分析,效率将会提高很多,但还是手动执行,也存在时间浪费的问题。</p>    <p>也可以使用 LeakCanary 或者腾讯自研的 LeakInspector 来测试,由于这两个工具是自动分析,效率会更加高效。但需要修改代码,把工具的 SDK 引入到工程中。此外针对不退出 Activity 的测试场景,不能很好的处理。</p>    <p>而我们的分析方法,一条命令就可以完成分析,并输出详细的泄露对象,以及调用关系。</p>    <p>从前面的表中可以看 LeakCanary 各方面都挺优秀,但是我们为什么没有使用它呢?这是因为 QQ 浏览器很多场景都是在 MainActivity 中进行的,比如打开网页,此时 MainActivity 并不会 Destroy ,直接使用 LeakCanary 无法进行泄漏分析,因此我们才考虑自己实现一个分析工具。</p>    <p>在 QQ 浏览器项目中,使用的分析工具叫做 HprofComparator ,这是我们自己开发的一个 Eclipse Application ,其原理与 Finder 的对比原理一致,但是加入了泄露分析的策略,可以通过命令行直接运行,更加的高效。</p>    <h3><strong>2.浏览器内存泄漏测试方案实施</strong></h3>    <p><strong>1) 测试场景选择</strong></p>    <p>做内存泄漏测试一定要全面的测试才敢通知项目组放心发布,目前 QQ 浏览器选择对所有的画面进行泄漏排查。</p>    <p>内存出现泄漏的前提条件一定是有新的内存分配,所以测试场景会选择有新对象创建的场景,并结合用户的使用场景和频率来确定优先级。测试场景主要有以下三种情况:</p>    <p>Ø  新画面打开</p>    <p>由于新的画面打开,就会创建新的 Activity ,并有许多其他对象被创建。</p>    <p>Ø  画面旋转</p>    <p>当屏幕旋转时, Orientation 设置发生了改变,当前显示的 Activity 会被重新创建。</p>    <p>Ø  滑动屏幕</p>    <p>滑动屏幕会使屏幕中显示的对象(比如浏览器小说阅读内容)创建。</p>    <p><strong>2)内存泄漏分析</strong></p>    <p>测试方法就是重复运行以上选择的场景,将整个测试过程分几个阶段获取到 Hprof 文件并保存。这个过程不详细阐述,下一部分会阐述我们的自动化方案。我们先看下拿到 Hprof 文件后人工分析的过程。</p>    <p>浏览器的内存分析工具运行的方法如下图所示。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/2b049cea273e7774756a634407ee7844.jpg"></p>    <p>运行完毕后将得到如下的分析结果文件。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/cf32a7ce6b95879d156a7cef3bf643cc.jpg"></p>    <p>需要重点查看的文件是 suspicion 文件,其内容如下图所示。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/697b128d48d557d6a3052aeed17561b1.jpg"></p>    <p>首先从图中可以看出 NewActivity 的对象泄漏了,该对象在操作过程中新创建出 5 个对象,内存增长了93696字节,内存中该对象的总数量为 13 个。</p>    <p>接着从图中的调用关系可以分析出,TextManager有一个listeners的数组持有了 NewActivity 的对象。</p>    <p>最后对比源代码查看输出结果可以一目了然的看出TextManager是一个单例的对象,而泄露对象应该是注册了 TextManager 的 ITextChangeListener,没有取消注册引起的泄漏。</p>    <p>我相信当测试人员把泄漏对象的根因连同 Bug 一起提交给开发时,开发同事们一定会觉得你很牛 X 。</p>    <p>此外泄露分析过程中还有一种常见情况,就是很多对象泄露是因为同一个原因,如下图是通过 Finder 对比得到的结果。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/a3128fb0cd78a4bb4747ab2b84d5730e.jpg"></p>    <p>从这个图中可以看出泄漏对象非常多,如果我们逐个对对象进行原因分析的话,将浪费大量的时间,但是实际上这些对象的泄漏都是因为 NewActivity 的泄漏造成的。为了提高我们的分析效率, HprofComparator 对泄漏对象进行了去重处理。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/9b5e975edcd6bcb61f3354379c10260a.png"></p>    <p>如上图所示, B 和 C 都为泄露嫌疑,从 B 和 C 的 Callstack 可以看出 B 是 C 的父对象( B 引用 C ),因此只要 B 释放了, C 也会释放。这种情况时我们就会将 C 合并到 B ,最终结果输出 B 。如前面的 suspicion 文件内容中显示,有92个对象被 duplicate 到了 NewActivity 上。这样做之后输出结果会被大量的精简,需要分析原因的对象数量将会大大减少,节省了许多的时间。</p>    <p><strong>3)关键字驱动的自动化方案</strong></p>    <p>在 QQ 浏览器项目中一开始还是人工获取 Hprof 文件,这样做的测试效率比较低。为了提高测试效率我们也引入了自动化测试,但如何才能让自动化测试投入产出比比较高呢?</p>    <p>在分析了我们的测试场景和测试执行过程后,我发现内存泄露测试基本上可以分为做操作和获取 Hprof 文件,而操作又分为点击、滑动、旋转屏幕、等待和输入文字等。整个测试过程中除了需要确保操作是正确的外,并不需要功能的验证。</p>    <p>基于这些特点,我选择关键字驱动的自动化方案,就是为了以简便的方式来实现自动化,减少开发和维护成本。</p>    <p>如下图所示,在测试打开小说书城的场景,需要启动程序,然后点击小说入口,然后返回,需要重复打开小说的过程。在脚本中直接省略了打开应用程序的操作, repeat 关键字说明后续的步骤是需要重复操作的,而点击小说入口则仅以“小说”替代。这样就使测试脚本足够简单,任何刚入门的测试人员都可以完成。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/479cf22363d7ddd222d84924d7f824ca.jpg"></p>    <p>在脚本要进行的操作都以关键字描述,测试时这些关键字都将转换成控件进行控件查找,或者转换成坐标直接点击。</p>    <p style="text-align:center"><br> <img src="https://simg.open-open.com/show/76ee4d17bfef5eba089c9d53aab31bbb.png"></p>    <p>以上的脚本还只是测试数据,我们需要一个测试驱动程序来分析测试脚本,以及执行对应的操作。在 QQ 浏览器项目中采用 Python 在实现驱动程序,并结合 Appium 实现自动化测试。驱动程序的执行流程非常简单,它就负责解析数据文件,然后执行关键字对应的操作。脚本的解析如下图的代码所示,每一个关键字会被处理成对应的命令,并存放到列表里。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/2b56bea3a75ab19004d72f2333e08374.jpg"></p>    <p>以上测试运行过程中还会伴随着截图,用于后期分析时确保测试是正确执行的。当测试运行完毕后, hprof 文件将被 Pull 到电脑上,并使用自动分析工具进行泄漏分析。</p>    <p><strong>4)测试运行情况</strong></p>    <p>目前内存泄露测试已经加入到每个版本的常规测试中,每个版本平均能扫描出10+内存泄漏的问题。</p>    <p>手动测试和自动化测试对比情况如下图所示,内存泄露自动化测试的效率提升主要体现两个方面:</p>    <p>Ø  测试效率提升。人工测试 150+ 用例大概需要 5 人日,而现在可以一晚上运行完毕。</p>    <p>Ø  分析效率提升。人工通过 Finder 进行分析,需要近 5 个人日,而现在仅需要 1 天就可以完成。</p>    <p>通过这样的方法不仅保证了测试的准确性,也保证了测试的全面性,整体测试效率提升 85% 。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/4aa88c81e5d24e12d45531d972bcd9fa.png"></p>    <h3><strong>3.后续思考</strong></h3>    <p>由于浏览器的内存泄漏分析方法与被测试的应用无关 , 后续将考虑实现泄漏分析平台化 , 这样可以让更多的项目快速的应用起来。</p>    <p>目前在浏览器项目使用了一种的基于模型的用例生成方法 , 但模型需要人工创建 ,以后 可以尝试对任意的应用程序自动生成模型并自动创建用例 .</p>    <p> </p>    <p>来自: http://www.tuicool.com/articles/ZBbyMra</p>    <p> </p>