引言
1. Android性能优化篇之内存优化--内存泄漏
2.Android性能优化篇之内存优化--内存优化分析工具
3.Android性能优化篇之UI渲染性能优化
4.Android性能优化篇之计算性能优化
5.Android性能优化篇之电量优化(1)——电量消耗分析
6.Android性能优化篇之电量优化(2)
7.Android性能优化篇之网络优化
8.Android性能优化篇之Bitmap优化
9.Android性能优化篇之图片压缩优化
10.Android性能优化篇之多线程并发优化
11.Android性能优化篇之数据传输效率优化
12.Android性能优化篇之程序启动时间性能优化
13.Android性能优化篇之安装包性能优化
14.Android性能优化篇之服务优化
介绍
上一章讲了内存相关的知识和内存泄漏的常见案例以及解决方法,我们写代码的时候要注意避免。但是我们不可能完全避免出现内存泄漏的情况,所以就要通过内存分析工具来分析是否产生了内存泄漏。主要讲的工具有以下几种:
Heap Snapshot
Heap Viewer
MAT
Allaction Tracking
TraceView
LeakCanary
Lint
下面我们就来一一讲解这些工具的使用:
1.Heap Snapshot
Heap Snapshot 是 android Studio 中 Android Monitor中的一个分析Java堆内存信息的工具,这边我们就用上章中的单例导致内存泄漏的例子来分析:
(1).Heap Dump启动
点击图中红色框中的Dump Java Heap,就会dump 出java堆内存信息文件(.hprof)
(2).分析Heap Snapshot面板中的信息
android studio会自动打开hprof文件,我们这时候就可以来重点分析我们需要检查的类是否有内存泄漏(必须要确定分析具体的类,比如我们分析MainActivity是否有内存泄漏,可以反复的改变屏幕的方向,然后dump出堆信息)
我们看到ClassName中可能数据太多,我们怎么能够定位到我们要分析的类呢,可以选择第二个红色框中的选择项来快的定位。
我们选择通过包来展示,这样可以通过包名来快速定位。
下面我们来分析下MainActivity是否有内存泄漏。
我们看到这里MainActivity有两个实例且都有深度,也就是说都被引用,根据Acitivity的生命周期原理,屏幕的旋转会回收之前的Activity,然后创建一个新的Activity,另一个一个被回收或者等待被回收,所以MainActivity肯定存在内存泄漏的问题。这个需要结合下面的引用树来看,但是这个太麻烦还不要用对,下面我们将介绍更好的工具来分析,这边只要知道我们分析的类是否发生内存泄漏就行了。
下面我们给出Snapshot中表中字段含意:Class Name 板块
Total Count 内存中该类的对象个数
Heap Count 堆内存中该类的对象个数
Sizeof 物理大小
Shallow size 该对象本身占有内存大小
Retained Size 释放该对象后,节省的内存大小
Instance 板块
depth 深度
Shallow Size 对象本身内存大小
Dominating Size 管辖的内存大小
2.Heap Viewer
Heap Viewer 是 Android Device Monitor 中的实时查看App分配的内存大小和空闲内存大小和发现内存泄漏的工具。
使用条件:1.必须5.0以及以上的系统,2.开发者选项可用使用步骤:
1.打开Android Device Monitor
2.点击DDMS
3.选中heap选项卡
4.选中我们的项目
5.点击update heap
6.点击Cause GC
下面解释下Heap面板中表示的意思:
A
Heap Size 堆栈分配给App的内存大小
Allocated 已分配使用的内存大小
Free 空闲的内存大小
%Used Allocated/Heap Size,使用率
Objects 对象数量
B 中 Type 类型
free 空闲的对象
data object 数据对象,类类型对象,最主要的观察对象
class object 类类型的引用对象
1-byte array(byte[],boolean[]) 一个字节的数组对象
2-byte array(short[],char[]) 两个字节的数组对象
4-byte array(long[],double[]) 4个字节的数组对象
non-Java object 非Java对象
B 中 表中列的表示意思
Count 数量
Total Size 总共占用的内存大小
Smallest 将对象占用内存的大小从小往大排,排在第一个的对象占用内存大小
Largest 将对象占用内存的大小从小往大排,排在最后一个的对象占用的内存大小
Median 将对象占用内存的大小从小往大排,拍在中间的对象占用的内存大小
Average 平均值
当我们点击某一行时,可以看到 C 的柱状图 ,表示 横坐标是对象的内存大小,这些值随着不同对象是不同的,纵坐标是在某个内存大小上的对象的数量
下面我们来发现有内存泄漏的单例例子:
(1).旋转屏幕前,我们看到已经分配的内存为814.617kb(手动GC):
(2).旋转屏幕后,我们看到已经分配的内存为956.891kb(手动GC):
我们对比前后的两次发现有内存不能被回收,也就是说旋转屏幕导致内存泄漏了。这个工具没法具体定位到内存泄漏的位置。
补充:Heap Viewer不光可以用来检测是否有内存泄漏,对于内存抖动,我们也可以用该工具检测,因为内存抖动的时候,会频繁发生GC,这个时候我们只需要开启Heap Viewer,观察数据的变化,如果发生内存抖动,会观察到数据在段时间内频繁更新
3.MAT
全称为Memory Analyzer Tool,一款详细分析Java堆内存的工具,该工具非常强大,为了使用该工具,我们需要hprof文件。但是该文件不能直接被MAT使用,需要进行一步转化,可以使用hprof-conv命令来转化,但是Android Studio可以直接转化.
下面我们就来那Heap Snapshot 中的hprof来分析:
(1).转换hprof文件
(2).使用Memory Analyzer Tool工具打开hprof文件
工具会自动生成一个可疑的内存泄漏,其实这里对我们用处不大,我们主要分析overView选项卡。
我们看到这里给出了一些基本的数据我大对象的图,我们主要分析下面的Actions中的三个工具:
Histogram
主要是列出每个类中对象,点击看下:
我们看到这边的对象有点多,我们选择按照包来显示,或者通过regex来筛选。
找到我们要分析的类,选择ListObject中的with incoming references,来显示引用MainActivity的类。
我们看到这边有两个地方引用了MainActivity,然后现在图中的选项,排除所有的soft,weak,phantom引用,看看当前的被什么引用。我们来看下两个结果:
我们看到一个被CommonUtil引用,导致内存泄漏,一个被InputMethodManger引用,导致内存泄漏,第二个是系统输入法的bug,而第一个是我们自己写的,如果列表中显示很多我们只能一一分析。
上面是分析一个hprof来看,可能有时也看不出来,那么我们可以通过两个hprof来对比分析。
对比分析
(1).将两个hprof文件添加到工具中
(2).将两个hprof文件添加到对比列表中
(3).对比分析
我们看到经过旋转操作,MainActivity有两个,所以这边肯定发生了内存泄漏,那到底是什么引起内存泄漏的呢?下面的分析步骤就和上面单独分析一个hprof文件一样了。
4.Allaction Tracking
Allocation Tracker(AS)工具比Allocation Tracker(Eclipse)工具强大的地方是更炫酷,更清晰,但是能做的事情都是一样的。所以我们就分析Android Studio中的。
(1).Allocation Tracker启动,生成alloc文件
(2).分析alloc文件
这边有两种显示的方式:
*Group by Method:用方法来分类我们的内存分配
*Group by Allocator:用内存分配器来分类我们的内存分配
首先以线程对象分类,默认以分配顺序来排序,当然你可以更改,只需在Size上点击一下就会倒序,如果以Count排序也是一样,Size就是内存大小,Count就是分配了多少次内存,点击一下线程就会查看每个线程里所有分配内存的方法
当你以Group by Allocator来查看内存分配的情况时,详细信息区域就会变成如下
这种方式显示的好处,是我们很好的定位我们自己的代码的分析信息
分类旁边还有跳转到源码的位置和统计图。
轮胎图
轮胎图是以圆心为起点,最外层是其内存实际分配的对象,每一个同心圆可能被分割成多个部分,代表了其不同的子孙,每一个同心圆代表他的一个后代,每个分割的部分代表了某一带人有多人,你双击某个同心圆中某个分割的部分,会变成以你点击的那一代为圆心再向外展开。如果想回到原始状态,双击圆心就可以了。
柱状图
柱状图以左边为起始点,从左到右的顺序是某个的堆栈信息顺序,纵坐标上的宽度是以其Count/Size的大小决定的。柱状图的内容其实和轮胎图没什么特别的地方
5.TraceView
从代码层面分析性能问题,针对每个方法来分析,比如当我们发现我们的应用出现卡顿的时候,我们可以来分析出现卡顿时在方法的调用上有没有很耗时的操作.
主要是下面两个问题:
- 调用次数不多,但是每一次执行都很耗时
- 方法耗时不大,但是调用次数太多
简单一点来说就是我们能找到频繁被调用的方法,也能找到执行非常耗时的方法,前者可能会造成Cpu频繁调用,手机发烫的问题,后者就是卡顿的问题.
(1).打开Android device monitor,选中我们的应用,点击start method profiling,开始trace
(2).操作我们的应用,再次点击上面的按钮,生成trace文件
traceview的面板分上下两个部分:
*时间线面板以每个线程为一行,右边是该线程在整个过程中方法执行的情况
*分析面板是以表格的形式展示所有线程的方法的各项指标
时间线面板
左边是线程信息,main线程就是Android应用的主线程,这个线程是都会有的,其他的线程可能因操作不同而发生改变.每个线程的右边对应的是该线程中每个方法的执行信息,左边为第一个方法执行开始,最右边为最后一个方法执行结束,其中的每一个小立柱就代表一次方法的调用,你可以把鼠标放到立柱上,就会显示该方法调用的详细信息,你可以随意滑动你的鼠标,滑倒哪里,左上角就会显示该方法调用的信息。
分析面板
面板列名含义:
Name 方法的详细信息,包括包名和参数信息
Incl Cpu Time Cpu执行该方法该方法及其子方法所花费的时间
Incl Cpu Time % Cpu执行该方法该方法及其子方法所花费占Cpu总执行时间的百分比
Excl Cpu Time Cpu执行该方法所话费的时间
Excl Cpu Time % Cpu执行该方法所话费的时间占Cpu总时间的百分比
Incl Real Time 该方法及其子方法执行所话费的实际时间,从执行该方法到结束一共花了多少时间
Incl Real Time % 上述时间占总的运行时间的百分比
Excl Real Time % 该方法自身的实际允许时间
Excl Real Time 上述时间占总的允许时间的百分比
Calls+Recur 调用次数+递归次数,只在方法中显示,在子展开后的父类和子类方法这一栏被下面的数据代替
Calls/Total 调用次数和总次数的占比
Cpu Time/Call Cpu执行时间和调用次数的百分比,代表该函数消耗cpu的平均时间
Real Time/Call 实际时间于调用次数的百分比,该表该函数平均执行时间
6.LeakCanary
quare公司开发的可以直接在手机端查看内存泄露的工具
实现原理:本质上还是用命令控制生成hprof文件分析检查内存泄露。
GitHub:https://github.com/square/leakcanary
使用:
(1).添加如下依赖
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
(2).添加Application子类
首先创建一个ExampleApplication,该类继承于Application,在该类的onCreate方法中添加如下代码开启LeakCanary监控:
LeakCanary.install(this);
具体的就不在说了,网上有很多,使用起来先对比较简单。
7.Lint
android stidio 自带的工具,功能很强大。
主要功能:
- 检测资源文件是否有没有用到的资源。
- 检测常见内存泄露
- 安全问题SDK版本安全问题
- 是否有没有使用的代码
- 代码的规范
- 自动生成的罗列出来
- 提示去除没用的导包
- 提示可能的bug
点击inpsect code,工具就会自动分析你的项目代码。
(1). correctness 有错误的代码
(2). performance 性能方面
(3). security 安全方面,常见的是备份
(4). usability 应该使用的方式
(5).class structure 类结构问题
(6). control flow issues 控制流程问题
(7). declaration redundancy 声明冗余
(8). imports 提示去除没用的导包
(9). probable bugs 可能的bug
(10). spelling 代码的规范,驼峰命名法等我们主要看下内存泄漏有没有提示:
我们看到这里直接提示我们可能出现的内存泄漏,很强大呀!
补充:在打包发布时,可以用Lint工具来去除多余的没有使用的资源和类,来减小apk的体积。