前言
经常有人将内存泄漏与内存溢出做区别,其实也是有道理的,毕竟不知道的人根本不知道这两者是啥,又都有“内存”呵呵呵(这里不要光他两比而忘记了内存抖动)。
Android泄漏其实需要结合一张图来看,Java判断对象是否可以回收使用的而是可达性分析算法。
在主流的商用程序语言中(Java和C#),都是使用可达性分析算法判断对象是否存活的。这个算法的基本思路就是通过一系列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,下图对象object5, object6, object7虽然有互相判断,但它们到GC Roots是不可达的,所以它们将会判定为是可回收对象。
在Java语言里,可作为GC Roots对象的包括如下几种:
a.虚拟机栈(栈桢中的本地变量表)中的引用的对象
b.方法区中的类静态属性引用的对象
c.方法区中的常量引用的对象
d.本地方法栈中JNI的引用的对象
导致泄漏的有:
- 单例导致内存泄露 (这个还真的必须泄漏)
- 静态变量导致内存泄露 (基本上静态都这样)
- 非静态内部类导致内存泄露
- 未取消注册或回调导致内存泄露
- Timer和TimerTask导致内存泄露
- 集合中的对象未清理造成内存泄露
-
资源未关闭或释放导致内存泄露
BroadCastReceiver、Cursor、Bitmap、IO流、自定义属性attribute attr.recycle()回收。 -
动画造成内存泄露
轮播图就很典型 -
Context导致的泄漏
经常我们会传Context给某个工具类,这个Context其实只有Service、Application与Activity有,但是Activity继承跟其它两个有点不一样,它继承的是子类,如下图
QQ截图20180319150011.png
回到话题上来,我这个Activity的Context被人所持有,那么我这个Activity肯定无法销毁,从使用上我们可以看出,这个玩意儿跟对象的实例差不多,所以我们如下写法就好了:
public static void showToast(Context context,String content) {
if (toast == null) {
toast = Toast.makeText(context.getApplicationContext(),content,Toast.LENGTH_SHORT);
} else {
toast.setText(content);
}
toast.show();
}
-
Handler导致的泄漏
http://blog.csdn.net/jdsjlzx/article/details/51386440 - 内部类持有外部类引用造成的泄漏
不管是匿名内部类还是“有名”内部类,如果其直接使用外部类任何变量的话,将会持有外部类引用,这样就可能导致外部类无法销毁,譬如回调方法
//A类中
final TextView tvA=find.....
B.getInstance().setOnClickListener(new onClickListener{
@overide
public viod onClick(){
tvA.setText("xxx")
}
})
//B类中
public class B{
private static final B instance;
private onClickListener listener;
public static B getInstance(){
if(instance==null){
instance=new B();
}
return intance;
}
private viod doXXX(){
if(listener!=null){
onClick();
}
}
public interface onClickListener{
void onClick();
}
public viod setOnClickListener(onClickListener listener){
this.listener=listener;
}
}
如果B类无法销毁或者B类无法短时间销毁导致A错过销毁,则销毁A的代码执行后无效果,进而导致内存泄漏
-
监听未移除
譬如:不需要用的监听未移除会发生内存泄露
//add监听,放到集合里面
tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
@Override
public void onWindowFocusChanged(boolean b) {
//监听view的加载,view加载出来的时候,计算他的宽高等。
//计算完后,一定要移除这个监听
tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
}
});
/***/
SensorManager sensorManager = getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
sensorManager.registerListener(this,sensor,SensorManager.SENSOR_DELAY_FASTEST);
//不需要用的时候记得移除监听
sensorManager.unregisterListener(listener);
网友评论