应用App内存的使用,也是评价一个应用性能高低的一个重要指标。虽然现在只能手机的内存越来越大,但是一个好的应用应当效率发挥到极致,精益求精。而现在有很多应用为了自己的利益,使用一些非常影响系统效率的方法,不仅败坏了Android的口碑,更极大地影响了系统的稳定性。例如某“X米”团购应用,在启动应用是会fork一个子线程,用于监听用户卸载应用。在KK下,该现场在卸载时不能被kill,而且每次启动都将fork新的进程,这就导致内存不断增高,极大地影响了低端机的使用体验。因此不管是什么应用,都应该把内存效率、用户体验放在首位,而不是为了满足自己的利益。
由于Android应用的沙箱机制,每个应用所分配的内存大小是有限度的,内存太低就会出发LMK——Low Memory Killer机制。那么到底什么是内存呢?通常情况下我们所说的内存是指手机的RAM,它包括以下几个部分。
速度最快的存储场所,因为寄存器位于处理器内部,在程序中无法控制。
存放基本类型的数据和对象的引用,但本身不存放在栈中,而存放在堆中。
堆内存用来存放new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收期(GC)来管理。
静态存储区是指在固定位置存放应用程序运行时一直存在的数据,在Java内存中专门划分了一个静态存储区来管理一些特殊的数据变量如静态的数据变量。
Java虚拟机必须为每个被卸载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集合,包括常量(基本类型,String)和对其他类型、字段、方法的符号引用。
在这些概念中,最容易被搞错的就是堆和栈的划分。当定义一个变量,Java虚拟机就会在栈中为该变量分配内存空间,当该变量作用域结束后,这部分内存控件马上被用作新的控件进行分配。如果使用new方式创建一个变量,那么就会在堆中为这个对象分配内存控件,即使该对象作用域结束,这部分内存也不会立即被回收,而是等待系统GC进行回收。堆的大小随着手机的不断发展而不断变大。在程序中,可以如用如下代码来堆的大小,所谓的内存缝隙,正式分析Heap中的内存状态。
ActivityManager manager = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
int heapSize = manager.getLargeMemoryClass();
Process Stats是KK上新增的一个内存监视服务,可以通过“Setting-Developeroptions-Process Stats”来开启该功能界面,如图所示。
同样,也可以使用Dumpsys命令来获取这些信息,命令如下所示。
adb shell dumpsys procstats
Meminfo也是系统上一个非常重要的内存监视工具,可以通过Settings-Apps-Running中打开这一界面,如图所示。
同样,也可以是用Dumpsys命令,命令如下所示。
adb shell dumpsys meminfo
adb pull /sdcard/dmtrace.trace trace.txt
对于导出的TraceView日志文件,可以使用SDK中的"sdk\tools\traceview.bat”工具来打开。或者在AMD工具中,在“file”菜单下选择“open file..."选项打开TraceView日志文件。
TraceView的分析界面分为两部分,上面是用于显示方法执行时间的时间轴区域,下面是显示详细信息的profile区域
时间轴区域如图所示。
时间轴区域显示了不同线程在不同的时间段内的执行情况,在时间轴中,每一行都代表了一个独立的线程。
使用鼠标滚轮可以放大时间轴,如图所示。
不同的色块代表了不同的执行方法,色块的长度,代表了方法所执行的时间。
Profile区域内显示了你选择的色块所代表的方法在该色块所处的时间段内的性能分析。如图所示。
在Profile区域中主要显示以下的信息。
每个时间都包含两列,一个是实际的时间,另一个是百分比。分析的时候,通常从Incl CPU Time和Calls+RecurCalls开始进行分析,对占用时间长的方法进行重点分析,如果占用时间长且Calls+RecurCalls次数少,那么就可以列为怀疑对象了。
MAT(Memory Analyzer Tool)工具是一个分析内存的强力助手。下载地址:MAT下载地址
首先打开Android Device Monitor工具,选择要监听的线程,并点击菜单栏中的”Update Heap“按钮,如图所示。
在Heap标签中,点击”Cause GC“按钮,就会显示出当前的内存状态,如图所示。
这里有一个判断当前是否存在内存泄漏的小技巧:当我们不停地点击”Cause GC“按钮时,如果”data object“一栏中的”Total Size"有明显变化,就代表可能存在明显变化,就代表可能存在内存泄漏。
上面时手动查看Heap状态,下面点击菜单栏的“Dump HPROF File”按钮,如图所示。
等待几秒后系统就会生成一个.hprof文件,我们要分析的就是这个文件。将它保存到PC上,默认包名为.hprof。不过对这个文件我们还不能直接使用MAT工具进行分析,还需要进行格式转换。在命令行下,切换到SDK目录的platform-tools目录下,使用hprof-conv工具帮助我们进行转换,命令如下所示:
hprof-conv F:\com.qisi.orderingsystem.a920server.hprof heap.hprof
命令格式为"hprof-conf infile outfile",使用生成的heap.hprof文件就可以利用MAT工具进行内存分析了。
打开MAT工具,选择“Open 啊 Heap Dump”选项,如图所示。
等待文件导入后,显示分析结果如图所示。
在上图中的报告里已经对整个内存状态进行了初始的分析,并给出了简要的分析结果。后面还可以进行更深入的分析,MAT功能强大,这里主要看以下几个功能。
Histogram直方图,用于显示内存中每个对象的数量、大小和名称。点击打开Histogram标签,如图所示。
在最上方一行,可以通过搜索过滤相应的关键字,这点在分析内存中时非常有用的,比如可以过滤“Order”关键字,查询结果如图所示。
在选择的对象上单击鼠标右键,在弹出的快捷菜单中选择“List objects-with incoming references"选项查看具体对象。
Dominator Tree支配树会将内存中的对象按照大小进行排序,并显示对象之间的引用对象之间的引用结构。点击打开Dominator Tree标签,显示如图10.28所示。
对象已经按照”Retained Heap“进行排序了,即按照对象及其所持有的引用的内存总和进行排序。通过分析内存占用大的对象来找出内存消耗的原因。
9.使用Dumpsys命令分析系统状态
使用Dumpsys命令可以列出Android系统相关的信息和服务状态。Dumpsys命令的功能非常强大,可以使用参数配置也非常多哦。
使用Dumpsys所支持的命令非常多,参数大致列举如下所示。
SurfaceFlinger
accessibility
account
activity
alarm
android.security.keystore
appops
appwidget
assetatlas
audio
backup
battery
batterypropreg
batterystats
bluetooth_manager
clipboard
commontime_management
connectivity
consumer_ir
content
country_detector
cpuinfo
dbinfo
device_policy
devicestoragemonitor
diskstats
display
display.qservice
dreams
drm.drmManager
dropbox
entropy
gfxinfo
hardware
input
input_method
iphonesubinfo
isms
location
lock_settings
media.audio_flinger
media.audio_policy
media.camera
media.player
media_router
meminfo
mount
netpolicy
netstats
network_management
nfc
notification
package
permission
phone
power
print
procstats
samplingprofiler
scheduling_policy
search
sensorservice
serial
servicediscovery
simphonebook
sip
statusbar
telephony.registry
textservices
uimode
updatelock
usagestats
usb
user
vibrator
wallpaper
wifi
wifip2p
window
使用Dumpsys命令时,只需要输入"adb shell dumpsys + 以上参数”即可。例如使用如下所示命令来获取Activity栈的详细信息。
adb shell dumpsys activity
下面的列表中,总结了一些常用的Dumpsys参数。
配合Linux下的Shell命令,如 “grep”、“find”等,可以让Dumpsys命令发挥非常大的左右。这进行性能绣花、Bug分析时时非常有用的。
性能优化时一个非常具有挑战性的工作,上面列举了很多分析内存、优化内存的方法,但真正的优化工作远不止这么简单。这里只是列举了一些入门的方法,而要进行完美的内存优化、内存分析绝非一日之功,需要开发者有身后的技术功底和耐心。