Android Monitor

MaureenTram 5年前
   <p>Android Studio 内置了四种性能监测工具Memory Monitor、Network Monitor、CPU Monitor、GPU Monitor,我们可以使用这些工具监测APP的状态,该文简单介绍下这些工具的使用</p>    <h2>Memory Monitor</h2>    <p>Memory Monitor工具主要是用来监测APP的内存分配情况,判断是否存在内存泄漏。连接设备,选择好要监测的APP,如图所示:</p>    <p><img src="https://simg.open-open.com/show/36f05b79298db07ed1c1a28684f36b21.png"></p>    <p>A:手动触发GC操作</p>    <p>B:获取当前的堆栈信息,生成.hprof文件</p>    <p>C:内存分配追踪工具,生成.alloc文件</p>    <p>D:已使用内存</p>    <p>E:剩余可用内存</p>    <p>通过与应用交互并在Memory Monitor中观察它是如何影响内存的使用,图表可以为你展示一些潜在的问题:</p>    <p>1.频繁的垃圾收集活动使应用运行缓慢。</p>    <p>2.应用耗尽内存导致app崩溃.</p>    <p>3.潜在的内存泄漏</p>    <p>正常情况下,上图中的D区域会随着时间的走势慢慢上升(就算你与APP没有任何交互),直到E区域被用完,则会触发GC操作,释放内存,周而复始。如果你发现你的应用是静态的,但是E区域的内存很快就被用完了,即频繁的触发GC操作,这时你就应该引起重视,说不定你的代码中就存在着引起内存泄漏的隐患。</p>    <h3>Dump Java Heap</h3>    <p>使用场景:定位内存泄漏</p>    <p>点击上图中的B按钮开始检测APP,此时APP会变得很卡,容易发生ANR,一段时间过后会生成.hprof文件,如下图所示</p>    <p><img src="https://simg.open-open.com/show/259313e860412fc3be8b198d97b7dfb4.png"></p>    <p>这里的截图是我故意生成的一个能引起内存泄漏的例子,点击上图右上方的Analyzer Tasks按钮,若代码中存在内存泄漏隐患,在其下方会列出可能引起内存泄漏的Activity,如上图右下方的Leaked Activities,之后我们便可以结合左下方Reference Tree中指出的问题分析,如果你有源码的话还可以索引源码(右键->Jump to source)。实例代码如下:</p>    <p><img src="https://simg.open-open.com/show/ccfa90274d9776c0ebe81fe78cfe91fc.png"></p>    <p>多次旋转屏幕,使得内存不断增加就容易引起内存泄漏。</p>    <p>上面的例子比较简单,可以直接通过Memory Monitor工具就能直接看出,在平常的开发中内存泄漏的问题往往没有这么简单,我们可以借助MAT工具分析。</p>    <h3>MAT</h3>    <p><a href="/misc/goto?guid=4959742507678824292" rel="nofollow,noindex">MAT(Memory Analyzer Tool)</a> ,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。</p>    <p>在使用MAT工具前,我们需要将.hprof文件转换成标准的.hprof文件才能被识别,在Android Studio中可以通过以下操作转换</p>    <p><img src="https://simg.open-open.com/show/b39a7b8167df2400c6537929b8f6cd64.png"></p>    <p>之后用MAT打开,显示如下</p>    <p><img src="https://simg.open-open.com/show/5087158a69caca16b0046ed2af95b970.png"></p>    <p>点击Histogram按钮</p>    <p><img src="https://simg.open-open.com/show/74564c8ac19f29a188dda610021bcaea.png"></p>    <p>如图所示,该图会列出内存中所有的对象的个数即其占用的内存大小,其次,我们可以输入指定的Activity名称来缩小定位范围</p>    <p><img src="https://simg.open-open.com/show/ae07a677c410ab797bc28138747b0ff7.png"></p>    <p>如图所示,这里列出了MainActivity和其内部类MyThread的对象个数即占用的内存大小,接下来我们选择一个条目右键—>Merge Shortest Paths to GC Roots(查看一个对象到GC Roots是否存在引用链相连接)->Merge Shortest Paths to GC Roots(排除虚引用/弱引用/软引用等等),</p>    <p><img src="https://simg.open-open.com/show/6ed90344b4169137e061902f713f1971.png"></p>    <p>如上图所示,就是可能存在内存泄漏的地方,具体还是要结合代码分析。</p>    <h3>GC Roots</h3>    <p>对象存活的判定:</p>    <p>当一个对象不会再被使用的时候,我们会说这对象已经死亡。对象何时死亡,写程序的人应当是最清楚的。如果计算机也要弄清楚这件事情,就需要使用一些方法来进行对象存活判定,常见的方法有 <strong>引用计数(Reference Counting)</strong> 和 <strong>有可达性分析(Reachability Analysis)</strong> 两种。</p>    <p>引用计数算法的大致思想是给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。</p>    <p>Java语言里面没有选用引用计数算法来管理内存,其中最主要原因是它没有一个优雅的方案解决对象之间相互循环引用的问题:</p>    <p>当两个对象互相引用,即使它们都无法被外界使用时,它们的引用计数器也不会为0。</p>    <p>可达性算法的基本思路就是通过一系列的称为GC根节点(GC Roots)的对象作为起始点,从这些节点开始进行向下搜索,搜索所走过的路径成为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。</p>    <p><img src="https://simg.open-open.com/show/b1b7625133fa9a83232138a344ca88fb.png"></p>    <p>如上图,对象object 5、object 6、object 7虽然互相有关联,它们的引用并不为0,但是它们到GC Roots是不可达的,因此它们将会被判定为是可回收的对象。</p>    <h3>Allocation Tracker</h3>    <p><img src="https://simg.open-open.com/show/7dece9167b4f21b83a317ef98832747a.png" alt="Android Monitor" width="550" height="223"></p>    <p>在内存图中点击C,启动追踪,再次点击停止追踪,随后自动生成一个alloc结尾的文件,这个文件就记录了这次追踪到的所有数据。</p>    <p><img src="https://simg.open-open.com/show/756aac3415b01f413404f12439c4995d.png"></p>    <p>如上图,我们可以看出这次操作中应用里的各个组件的分配次数与占用大小,若发现这两个数据有异常(分配过多,占用过大),同样可以索引源码优化(前提是你有),最下方的视图是以另一种较酷炫的方式呈现,感兴趣的可以具体结合文件操作。</p>    <h2>Network Monitor</h2>    <p>Network Monitor是用于显示app网络请求的状态,频繁的网络请求是耗电的重要原因</p>    <p><img src="https://simg.open-open.com/show/5471726b789326e2b679f033d568283c.png"></p>    <p>如上图所示,Tx与Rx分别表示上下行的速度。</p>    <h2>GPU Monitor</h2>    <p>GPU Monitor工具可以将进行UI渲染工作所花的时间表现出来,它记录下渲染线程准备以及进行描绘的时间。</p>    <p>Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,为了能够实现60fps,这意味着程序的大多数操作都必须在16ms内完成。</p>    <h3>Why 60fps?</h3>    <p>我们通常都会提到60fps与16ms,可是知道为何会是以程序是否达到60fps来作为App性能的衡量标准吗?这是因为人眼与大脑之间的协作无法感知超过60fps的画面更新。</p>    <p>12fps大概类似手动快速翻动书籍的帧率,这明显是可以感知到不够顺滑的。24fps使得人眼感知的是连续线性的运动,这其实是归功于运动模糊的效果。24fps是电影胶圈通常使用的帧率,因为这个帧率已经足够支撑大部分电影画面需要表达的内容,同时能够最大的减少费用支出。但是低于30fps是无法顺畅表现绚丽的画面内容的,此时就需要用到60fps来达到想要的效果,当然超过60fps是没有必要的。</p>    <p>开发app的性能目标就是保持60fps,这意味着每一帧你只有16ms=1000/60的时间来处理所有的任务。</p>    <h3>Get GPU Trace</h3>    <p><img src="https://simg.open-open.com/show/d056f555f3c23c371469bf2558545fe2.png"></p>    <p>如上图所示,就是GPU Monitor的样子,点击上图获取gfxtrace按钮后,出现弹出框让你选择要监测的指定线程</p>    <p><img src="https://simg.open-open.com/show/c692ed89493994aa90659ec5c55c226b.png"></p>    <p>选定后点击Trace</p>    <p><img src="https://simg.open-open.com/show/ec642aa868413e012ce9dc1fb1411d4c.png"></p>    <p>过程中会加载指定的lib,同时手机会弹出Dialog</p>    <p><img src="https://simg.open-open.com/show/1aeb4d5ec6a91100fbd6bfcb77c35440.png"></p>    <p>环境需满足条件(手机root,安装GPU Debugging tools以及相关lib),若不满足会提示:“Failed to connect to the graphics debugger”,假设这里已满足条件</p>    <p><img src="https://simg.open-open.com/show/b1ffd5bbc882aa768f86b05ee86152cd.png"></p>    <p>判断是否在获取trace的依据是随着你的操作,上图的值不断增长,点击stop获得这个过程中生成的gfxtrace。</p>    <h2>GPU Debugger</h2>    <p>GPU Debuger是检查OpenGL ES 2.0或3.1渲染app图形的情况,打开之前生成的gfxtrace</p>    <p><img src="https://simg.open-open.com/show/43f7e0fc4c36c30e365bde47b3dc022f.png"></p>    <p>渲染上下文:</p>    <p>渲染上下文是执行OpenGL ES命令所需的。它用来收集渲染一张图片所需的状态,包括相关的缓存区、阴影、纹理等。许多游戏应用程序只有一个上下文。更高级的应用程序可以使用超过一个上下文。</p>    <p>如果你选择了一个上下文,在GPU Commands 面板中的调用方法将按照调用顺序排列。</p>    <p>渲染时间线:</p>    <p>渲染时间线中的缩略图和GPU Commands 面板下的Frame一一对应,GPU Commands 面板显示了 OpenGL ES 生成每一帧的调用层级。</p>    <p>支持双buffer的apps渲染一帧的结尾函数是eglSwapBuffers()方法。</p>    <h3>Framebuffer Pane</h3>    <p>在GPU Commands面板中选择一帧(生成这一个由多个Draw函数完成,在Draw函数中又有多个openGL ES方法),Framebuffer 面板显示的内容取决于最后一个方法。</p>    <p><img src="https://simg.open-open.com/show/54531c47fe2d7add97baa015a63da84e.png"></p>    <p>对于上图红框中各项工具的具体作用可以查看 <a href="/misc/goto?guid=4959742507772382348" rel="nofollow,noindex">官网</a> 的描述,本地测试的时候作用不够明显。</p>    <p><img src="https://simg.open-open.com/show/de60cf453c7447e1ff4537a28c10b8dd.png"> <img src="https://simg.open-open.com/show/6bb51165ad160e31f54d720d5b676218.png"></p>    <h3>Textures Pane</h3>    <p><img src="https://simg.open-open.com/show/0202462588bbb55e15a4d98f6b155dfc.png"></p>    <p><img src="https://simg.open-open.com/show/dfe596b45b05b4db6e1bbb6bbd32f57b.png"></p>    <h3>Geometry pane</h3>    <p><img src="https://simg.open-open.com/show/0611958c76a235d579b9e023e4eabba7.png"></p>    <p>几何面板的介绍同样本地的操作不够直观,可以直接查看 <a href="/misc/goto?guid=4959742507772382348" rel="nofollow,noindex">官网</a> 介绍</p>    <p><img src="https://simg.open-open.com/show/a4eb995775892b57f6cb18f79a2a819d.png"> <img src="https://simg.open-open.com/show/5bfa1c9c4c8a2b497d3bba9892e4baa1.png"> <img src="https://simg.open-open.com/show/612574f875dc1c0f0fbd9949c72987a2.png"></p>    <h3>GPU state pane</h3>    <p>查看当前GPU的状态</p>    <p><img src="https://simg.open-open.com/show/3fbb2b70cca57eb55dbce02728621c69.png"></p>    <h3>Memory pane</h3>    <p>查看所选方法的值在内存中的保存情况</p>    <p><img src="https://simg.open-open.com/show/669448ddb916aec649290cc6ee22e271.png"></p>    <h2>CPU Monitor</h2>    <p>CPU Monitor可以对代码中的方法进行检测,同样可以生成一个Trace文件</p>    <p><img src="https://simg.open-open.com/show/edad211797dbb6cd8881bd42f7a3edcc.png"></p>    <p>生成的Trace文件如下图</p>    <p><img src="https://simg.open-open.com/show/3d338ab4a884eebc51441d818a07896d.png"></p>    <p>通过方法的调用次数和所花时间来查看,通常判断方法是:</p>    <p>1.如果方法调用次数不多,但每次调用却需要花费很长的时间的函数,可能会有问题。</p>    <p>2.如果自身占用时间不长,但调用却非常频繁的函数也可能会有问题。</p>    <p> </p>    <p>来自:http://rkhcy.github.io/2017/02/14/Android Monitor/</p>    <p> </p>