之前说了内存泄漏和检测工具,这里就记录一下常见的内存泄露有哪些
前言
在举例子以前,需要明白两个概念
- 内存泄露(
Memory Leak):某些对象已经不再使用,但仍然直接或间接的被引用到GC ROOT中,此时GC没法对其进行回收,就造成了内存泄露 - 内存溢出(
OOM):当应用占用了大于虚拟机分配的内存空间时,会造成内存溢出 
静态变量引起的内存泄露
当调用getInstance时,如果传入的context是Activity的context。只要这个单利没有被释放,那么这个Activity也不会被释放一直到进程退出才会释放1
2
3
4
5
6
7
8
9
10
11
12
13public class CommUtil {
	private static CommUtil instance;
	private Context context;
	private CommUtil(Context context){
		this.context = context;
	}
	public static CommUtil getInstance(Context mcontext){
		if(instance == null){
			instance = new CommUtil(mcontext);
		}
		return instance;
	}
}
非静态内部类引起内存泄露(包括匿名内部类)
错误的示范:1
2
3
4
5
6
7
8
9
10
11
12
13
14public void loadData(){
	new Thread(new Runnable() {
		
		public void run() {
			while(true){
				try {
					//处理数据
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}).start();
}
当调用这个方法的时候,这个内部类会持有调用方法的类,可能就会造成内存泄露
解决方案:将非静态内部类修改为静态内部类(静态内部类不会隐式持有外部类)1
2
3
4
5
6
7
8
9
10
11
12
13
14public static void loadData(){
	new Thread(new Runnable() {
		
		public void run() {
			while(true){
				try {
					//处理数据
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}).start();
}
其实就是非静态方法的生命周期短于当前类而造成的内存泄露
还有另一种情况,线程延迟执行的情况,此时已经加载到执行队列,比如:1
2
3
4
5
6
7
8
9
10
11
12
13
14public static void loadData(){
	new Timer().schedule(new TimerTask() {
		
		public void run() {
			while(true){
				try {
					//处理数据
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}, 20000);
}
那么这种情况就需要在onDestroy方法中把timer.cancel掉然后赋空
在使用Handler时候也存在这样的情况,例如1
2
3
4
5
6
7
8
9
10
11private Handler mHandler = new Handler(){
	
	public void handleMessage(Message msg) {
		super.handleMessage(msg);
		switch (msg.what){
		case 0:
			//处理数据
			break;
		}
	}
};
如果在其外部调用mHandler.sendEmptyMessage(0);或者mHandler.sendMessageAtTime(msg,10000);方法的时候,此时mHandler作为内部类,持有外部类的引用,如果其Activity退出,那么mHandler可能还存在,那么此时就会一直持有Activity,就造成了内存泄露
解决办法:
- 与其他处理方式一样,将其设置为静态内部类
 - 如果在其内部需要使用外部类的资源时,将
Handler设置为弱引用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23private static class MyHandler extends Handler{
//private MainActivity mainActivity;//直接持有了一个外部类的强引用,会内存泄露
private WeakReference<MainActivity> mainActivity;//设置弱引用保存,当内存一发生GC的时候就会回收
public MyHandler(MainActivity mainActivity) {
this.mainActivity = new WeakReference<MainActivity>(mainActivity);
}
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity main = mainActivity.get();
if(main == null || main.isFinishing()){
return;
}
switch (msg.what){
case 0:
//处理数据
int b = main.a; //引用外部类的资源
break;
}
}
} 
不需要用的监听未移除会发生内存泄露
例子1:1
2
3
4
5
6
7
8
9final TextView tv =  (TextView) findViewById(R.id.tv);
tv.setOnClickListener(); //监听执行完回收对象
tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
	
	public void onWindowFocusChanged(boolean b) {
		//监听处理,处理完成后要移除监听
		tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
	}
});
如果要设计ListenerCollector,那么一般使用WeakHashMap,WeakHashMap除了自身有对key的引用外,此key没有其他引用那么此map会自动丢弃此值1
2
3
4
5
6
7
8
9
10
11
12public class ListenerCollector {
    static private WeakHashMap<View, MyView.MyListener> sListener = new WeakHashMap<>();
    public void setsListener(View view, MyView.MyListener listener) {
        sListener.put(view, listener);
    }
    public static void clearListeners() {
        //移除所有监听
        sListener.clear();
    }
例子2:1
2
3
4
5SensorManager sensorManager = getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
sensorManager.registerListener(this,sensor,SensorManager.SENSOR_DELAY_FASTEST);
//不需要用的时候移除监听
sensorManager.unregisterListener(listener);
资源未关闭引起的内存泄露情况
BroadCastReceiver、Cursor、Bitmap、IO流、自定义属性attribute等
当不需要使用的时候,及时释放资源,否则就会内存泄露
无限循环动画
没有在onDestroy中停止动画,否则Activity就会变成泄露对象,比如:轮播图效果