前言通过这几天对几个应用程序的内存泄漏检测和改进,效果很明显:当应用程序完全退出时,手动触发GC,内存占用从100M以上减少到最低20M;手动触发GC后,通过adbshelldumpsysmeminfopackagename-d的Activity和View数也接近于0(没有重置为0是因为SDK存在内存泄漏,需要通过adbshell处理)中间层);foundFoundamemoryleakintheSDK(AndroidInputMethodManager导致的内存泄漏及其解决方法);发现MTKWebview内存泄漏(org.chromium.android_webview.AwPasswordHandler.java中privatestaticAwPasswordHandlersInstance=null导致的内存泄漏)。从结果来看,我分析和改善内存泄漏的方法是正确的。这个过程并不复杂,所以整理一下总结出来分享一下。原则对于性能问题,分析和改进必须遵循以下原则:一切靠数据,不能跟着感觉走。觉得有问题就改,很可能适得其反;性能优化是一个持续的过程,需要不断改进,不要一蹴而就;对于性能问题,不一定非要改进,有些问题可能因为架构限制或者其他原因很难改进,首先要保证能用,再考虑好用。改进之后一定要进行验证,任何一个地方的改动都需要进行验证,避免因为性能问题改进带来的其他问题。以下步骤是我解决内存泄漏性能问题的步骤:优先处理常见的内存泄漏。首先,解决常见的内存泄漏问题。可以借助AndroidStudio的Analyze-InspectCode静态分析此过程。常见的内存泄漏问题包括:非静态内部类引起的内存泄漏,比如Handler,解决方法是将内部类写成静态内部类,在静态内部类中使用软引用/弱引用来保存实例外部类,例如:staticclassExerciseHandlerextendsHandler{privateSoftReferenceexerciseActivitySoftReference=null;publicExerciseHandler(ExerciseActivityexerciseActivity){exerciseActivitySoftReference=newSoftReference(exerciseActivity);}@OverridepublicvoidhandleMessage(Messagemsg){ExerciseActivityexerciseActivity();=exerciseActivity){super.handleMessage(msg);switch(msg.what){caseMSG_XX:exerciseActivity.***;break;default:break;}}}}IO操作后,未关闭文件导致的内存泄漏,比如Cursor,FileInputStream,FileOutputStream在使用Close后没有使用,这种问题在Android中可以通过静态代码分析来检测Studio2.0,可直接改进;在自定义View中使用TypedArray后,没有recycle,这种问题也可以通过AndroidStudio2.0中的静态代码分析检测出来,直接改进即可;有些地方使用了四大组件的上下文,离开这些组件后,仍然持有上下文导致的内存泄漏。问题属于共识。在编写代码的过程中,应该遵守规则。使用ApplicationContext可以解决这种内存泄漏问题。至于什么时候应该使用四大组件的Context,什么时候应该使用ApplicationContext,可以参考下表:应用使用场景备注:请注意,有些NO后面加了一些数字。其实这些在能力上都是YES,但是为什么说是NO呢?下面一一解释:1、编号1:startActivity在这些类中是可用的,但是需要创建一个新的task,一般不推荐;2.编号2:在这些类中布局inflate是合法的,但是会使用系统默认的主题样式。某些样式被定义可能不会被使用;3.数字3:当Receiver为空时允许。4.2及以上版本,用于获取粘播当前值(可忽略);4.ContentProvider、BroadcastReceiver之所以在上表,是因为在其内部方法中有context可以使用。还有一种内存泄漏不属于内存泄漏,但在分析内存泄漏问题时应该一起解决:同一个app将图片放在不同的drawable文件夹中,同一个设备占用的内存是不同的。参见:Android中图片大小、内存占用和drawable文件夹关系的研究与分析。解决这个问题,可以遵循以下原则:1.UI只提供一组高分辨率图片,建议将图片放在drawable-xxhdpi文件夹下(不一定要放在drawable-xxhdpi文件夹下)xxxhdpi或更高分辨率的文件夹,权衡利弊,只管主流设备),这样图片在低分辨率设备上的大小只是压缩,不会有内存增加;2、涉及桌面插件或者不需要缩放的图片放在drawable-nodpi文件夹下,该文件夹下的图片在任何设备上都不会缩放。程序运行后使用该工具检查内存泄漏。通过以上步骤,应用中的大部分内存泄漏问题都可以得到解决。还有一些内存泄漏需要通过运行程序,运行后分析内存快照来解决。比如注册后没有内存泄漏。反注册、类中静态成员变量引起的内存泄露、SDK内存泄露等解决这类问题可以分两步进行:首先使用内存泄漏检测工具定位问题。检测内存泄漏有两种方便的方法:1.一种是使用开源项目Leakcanary,需要在代码中加入。运行后生成分析结果;2、另一种方法是在进入一个界面前使用adbshelldumpsysmeminfopackagename-d命令查看Activity和View数,退出该界面后再次查看Activity和View数。比较进入前后Activity和View数的变化。如果有差异,说明存在内存泄漏(记得在使用命令检查Activity和View数之前手动触发GC)。备注:在AndroidStudio中,可以通过以下方式获取当前选中进程的内存信息:然后使用MAT对程序运行进行内存快照,进行详细分析。网上有很多关于MAT使用的优质文章,比如:Android性能优化在使用MAT分析内存泄漏之前,需要知道以下几点:1.不要指望MAT告诉你在哪里有内存泄漏。这就需要你先根据上一步定位到可能存在内存泄漏的类。然后用MAT确认是否有内存泄漏,哪里有内存泄漏;2.利用RetainedSize分析某个类及其相关实例消耗的内存。如果这个类的RetainedSize比较大,优先解析;3.检查类是否存在内存泄漏时,排除其软/弱/幻引用,右击某个类→MergeShortestPathstoGCRoots→excludeallphantom/weak/softetc.references。验证改进效果根据个人经验,我一般都是这样验证改进效果,运行程序,再运行各个函数,确定没有问题,完全退出程序,手动触发GC,然后查看Activivites和Activivites通过adbshelldumpsysmeminfopackagename-dView数是否接近0;如果不为0,通过Leakcanary检查可能的内存泄漏,通过MAT继续分析,重复直到满意为止。推荐阅读SpeedupyourappAndroidperformanceoptimizationusingMATtoanalyzememoryleaks