内存优化

####内存优化

一. 避免内存泄漏及优化方法

单例导致内存泄露
单例的静态特性使得它的生命周期同应用的生命周期一样长,如果一个对象已经没有用处了,但是单例还持有它的引用,那么在整个应用程序的生命周期它都不能正常被回收,从而导致内存泄露。

内存泄漏场景
单例模式引用了activity,activity退出后,有于单例引用而无法释放

解决方法
context参数改为全局的上下文

静态变量导致内存泄露

静态变量存储在方法区,它的生命周期从类加载开始,到整个进程结束。一旦静态变量初始化后,它所持有的引用只有等到进程结束才会释放。

内存泄漏场景
activity中定静态变量,activity退出后,有于静态变量引用而不能释放

非静态内部类导致内存泄露
非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。

内存泄漏场景
1.典型的场景就是使用Handler

1.使用Thread或者AsyncTask。

解决方法
静态内部类+弱引用的方式
hadler 要清除回调消息

未取消注册或回调导致内存泄露
比如我们在Activity中注册广播,如果在Activity销毁后不取消注册,那么这个刚播会一直存在系统中,同上面所说的非静态内部类一样持有Activity引用,导致内存泄露。因此注册广播后在Activity销毁后一定要取消注册。

Timer和TimerTask导致内存泄露
Timer和TimerTask在Android中通常会被用来做一些计时或循环任务,比如实现无限轮播的ViewPager:

当我们Activity销毁的时,有可能Timer还在继续等待执行TimerTask,它持有Activity的引用不能被回收,因此当我们Activity销毁的时候要立即cancel掉Timer和TimerTask,以避免发生内存泄漏。

集合中的对象未清理造成内存泄露
这个比较好理解,如果一个对象放入到ArrayList、HashMap等集合中,这个集合就会持有该对象的引用。当我们不再需要这个对象时,也并没有将它从集合中移除,这样只要集合还在使用(而此对象已经无用了),这个对象就造成了内存泄露。并且如果集合被静态引用的话,集合里面那些没有用的对象更会造成内存泄露了。所以在使用集合时要及时将不用的对象从集合remove,或者clear集合,以避免内存泄漏。

资源未关闭或释放导致内存泄露
在使用IO、File流或者Sqlite、Cursor等资源时要及时关闭。这些资源在进行读写操作时通常都使用了缓冲,如果及时不关闭,这些缓冲对象就会一直被占用而得不到释放,以致发生内存泄露。因此我们在不需要使用它们的时候就及时关闭,以便缓冲能及时得到释放,从而避免内存泄露。

属性动画造成内存泄露
动画同样是一个耗时任务,比如在Activity中启动了属性动画(ObjectAnimator),但是在销毁的时候,没有调用cancle方法,虽然我们看不到动画了,但是这个动画依然会不断地播放下去,动画引用所在的控件,所在的控件引用Activity,这就造成Activity无法正常释放。因此同样要在Activity销毁的时候cancel掉属性动画,避免发生内存泄漏。

WebView造成内存泄露
关于WebView的内存泄露,因为WebView在加载网页后会长期占用内存而不能被释放,因此我们在Activity销毁后要调用它的destory()方法来销毁它以释放内存。

总结
内存泄露在Android内存优化是一个比较重要的一个方面,很多时候程序中发生了内存泄露我们不一定就能注意到,所有在编码的过程要养成良好的习惯。总结下来只要做到以下这几点就能避免大多数情况的内存泄漏:

构造单例的时候尽量别用Activity的引用;
静态引用时注意应用对象的置空或者少用静态引用;
使用静态内部类+软引用代替非静态内部类;
及时取消广播或者观察者注册;
耗时任务、属性动画在Activity销毁时记得cancel;
文件流、Cursor等资源及时关闭;
Activity销毁时WebView的移除和销毁。

自己写一遍加深印象方便记忆
感谢原文作者
原文链接:https://www.jianshu.com/p/ab4a7e353076

内存分析工具

Android Profiler
leakcanary

二.对象复用

复用系统自带的资源
例如adapter中的ContentView;避免在onDraw里创建对象;对象池设计的应用。

三.数据结构优化

频繁字符串拼接使用stringbuilder,它比+拼接高效;大数据量存储使用ArrayMap,SparseArray替代HaspMap;避
免内存抖动,避免大量分配了一些局部变量。

四.缓存技术

1.使用缓存技术,比如LruCache、DiskLruCache、对象重复并且频繁调用可以考虑对象池
2.对于引用生命周期不一样的对象,可以用软引用或弱引用SoftReferner WeakReferner

SoftReferner 和WeakReferner弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

ReferenceQueue

引用队列,如果保存的是Reference对象本身,如果:Reference引用指向的对象被GC回收,其实Reference已经无效了

leakcanary,每当弱引用所指向的对象被内存回收的时候,我们就可以在queue中找到这个引用。如果在接下来的预期时间之后,我们发现它依然没有出现在ReferenceQueue中,那就可以判定它的内存泄露了。LeakCanary检测内存泄露的核心原理就在这里

GC回收机制算法 - 可达性算法
可达性算法是目前Java虚拟机比较常用的GC回收机制算法。

主要原理是是通过GCRoots对象作为根节点,从根节点开始往下遍历,遍历的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的时候,则说明此对象是不可用的。

四.避免oom

避免方法 上面的内存优化