文章目录
一、内存泄露
内存泄漏:长生命周期的对象引用了短声明周期的对象,导致短生命周期的对象的工作在执行完后,无法在GC时被正常回收。
如何避免内存泄漏:
列举几个内存泄漏常见场景。
内部类引用导致Activity的泄漏。
内部类的优势是可以访问外部类,但内部类持有外部类实例的强引用往往导致内存泄漏。
Activity Context被传递到其他实例中。
这可能导致自身被引用而发生泄漏。所以要使用Application的Context。
临时Bitmap对象的及时回收。
注意临时Bitmap对象的及时回收(recycle掉),从而减轻GC的负担。
注意WebView的泄漏。
WebView的内部有一个类叫BrowserFrame,是Handler的子类,WebViewCoreThread通过它与Activity交互。如果Activity退出前,WebViewCoreThread处理了一个延时消息,就会造成内存泄漏。
注意Cursor、File是否及时关闭。
二、内存溢出
虚拟机为每个应用分配的内存是有限的,如果使用超过了这个限度就会发生内存溢出(OOM)。常见避免内存溢出的措施有:
使用更加轻量的数据结构。
比如SparseArray ArrayMap替代HashMap。
SparseArray通过两个数组,一个存储key(int型),另外一个存储value。二分查找法存储和读取数据。put数据的时候,按照key从小到大的顺序排列好。
ArrayMap原理跟SparseArray相似,它的数组mHash存储的是hash值,另一个数组mArray存mHash对应未知的key和value。HashMap在扩容时重新创建了对象,而ArrayMap对数组进行了复制。另外ArrayMap提供了数组收缩的功能,在clear或remove后,会重新收缩数组,释放空间。
SparseArray
1 SparseArray 内部使用双数组,分别存储 Key 和 Value,Key 是 int[],用于查找 Value 对应的 Index,来定位数据在 Value 中的位置。
2 使用二分查找来定位 Key 数组中对应值的位置,所以 Key 数组是有序的。
明了 SparseArray 内部使用二分查找,在 mKey 数组中定位 key 的位置。又因为需要支持二分查找,所以 mKey 数组内存储的数据,必须是有序的。所以在 put() 操作的时候,就需要通过数组插入的方式,来保证数据的有序。又因为使用了数组结构,在数据空间不够时,还需要采取动态扩容的方式,采用新旧数组复制的方式,增大数组的空间,这部分操作都封装在 GrowingArrayUtils 的 insert() 方法中。
3 使用数组就要面临删除数据时数据搬移的问题,所以引入了 DELETE 标记。
引入 DELETE 标识是为了减少删除数据时数据的搬移次数,而这必然带来了数组的「空洞」,为了解决这个问题,又需要在适当的时候触发 GC 操作,来回收 DELETE 标识。
Bitmap和ImageLoader框架
Bitmap是安卓中的图片,因为占用空间大,如果处理不好大图片或者大量图片,很容易造成OOM。
使用合适大小的图片,使用合适编码的Bitmap。
在加载时进行多级缓存。例如:Universal-Image-Loader框架。
###避免内存泄露
内存泄漏会导致内存很多空间被浪费。
###其他
- 当界面不可见时释放内存、当内存紧张时释放内存
- 避免在很小的ImageView上显示一张高分辨率的图片
- 避免在Android里面使用Enum
- 免在onDraw方法里面执行对象的创建:迅速增加内存的使用,而且很容易引起频繁的gc
- 复用系统自带的资源
- 注意在ListView ConvertView的复用
- StringBuilder StringBuilder来替代频繁的“+
- 谨慎使用large heap
- 资源文件需要选择合适的文件夹进行存放
- Try catch某些大内存分配的操作。在可能发生OOM的地方进行异常处理。
Universal-Image-Loader
工作流程:
①UI:请求数据,使用唯一的Key值索引Memory Cache中的Bitmap。
② 内存缓存:缓存搜索,如果能找到Key值对应的Bitmap,则返回数据。否则执行第三步。
③ 硬盘存储:使用唯一Key值对应的文件名,检索SDCard上的文件。
④ 如果有对应文件,使用BitmapFactory.decode*方法,解码Bitmap并返回数据,同时将数据写入缓存。如果没有对应文件,执行第五步。
⑤ 下载图片:启动异步线程,从数据源下载数据(Web)。
⑥ 若下载成功,将数据同时写入硬盘和缓存,并将Bitmap显示在UI中。
内存缓存LruMemoryCache:
LruMemoryCache使用LinkedHashMap来缓存数据。当LinkedHashMap的accessOrder==true时,每一次get或put操作都会将所操作项移动到链表的尾部(链表头被认为是最少使用的,链表尾被认为是最常使用的。),当内存不够时优先提出链表头部的数据。
磁盘缓存UnlimitedDiscCache:
它的fileNameGenerator通过对象的哈希值来命名,避免重复。
性能分析工具
LeakCanary
LeakCanary是一个开源类库,用以检测内存泄漏。
TraceView
TraceView可以让我们查看方法执行的时间。
三、内存抖动
内存抖动是由于短时间内有大量对象进出Young Generiation区导致的,它伴随着频繁的GC。
避免内存抖动
- 尽量避免在循环体内创建对象,应该把对象创建移到循环体外。
- 注意自定义View的onDraw()方法会被频繁调用,所以在这里面不应该频繁的创建对象。
- 当需要大量使用Bitmap的时候,试着把它们缓存在数组中实现复用。
- 对于能够复用的对象,同理可以使用对象池将它们缓存起来。