android 内存泄漏详解

一款音乐播放器,基于5.0新特性,效果炫酷,点击看源码

Java语言是垃圾回收语言的一种,好处就是开发者不用特意的管理内存的分配,但是java仍然存在很多内存泄漏的可能,不好好处理内存泄漏的问题,最终会导致app的奔溃。

内存泄漏与内存溢出的区别

内存泄漏:程序向系统申请分配内存空间后(new 创建对象申请内存空间),在使用完毕后,申请的内存空间没有释放掉,其他的程序也无法使用这部分内存空间,直到程序结束。

内存溢出:程序向系统申请的内存空间超出了系统能给的,就会内存溢出(OOM)。

大量的内存泄漏会导致内存溢出(OOM);

垃圾回收机制

大家都知道java中的栈存放基本数据类型变量和对象的引用,堆存放引用的对象。
Java垃圾回收可以自动清空堆中不在使用的对象。换句话说就是可以自动清理没有引用指向的对象。比如被置空的对象。
为了可以更加灵活的控制对象的生命周期,JDK1.2将对象的引用分为四个级别:强引用,软引用,弱引用,虚引用.

1.强引用

强引用是最常见的一种引用类型。比如:Student stu = new Student();强引用自己存储在栈内存中,存储的地址指向堆内存中的对象。当堆内存中的对象没有任何强引用指向时,或者 student = null时,系统进行垃圾回收时,这部分堆内存就会被回收掉。

2.软引用

软引用一般使用形式:

Student stu = new Student();
SoftReference<Student> soft = new SoftReference<Student>(stu);

持有软引用的对象进行垃圾回收需要的条件:
1.没有强引用对象指向它
2.当虚拟机内存不足时
所以软引用指向的对象占据堆内存的时间延长了,直到虚拟机内存不足时,才会被回收掉。

3.弱引用

弱引用一般使用形式:

Student stu = new Student();
WeakReference<Student> weak = new WeakReference<Student>(stu);

弱引用不改变原有强引用对象的回收时机,一旦其指向对象没有任何强引用对象时,就可以被回收掉。

4.虚引用

虚引用PhantomReference必须结合ReferenceQueue使用,同样不会改变其指向对象的回收时机。有两个特点:
1.PhantomReference只有一个构造函数

PhantomReference(T referent, ReferenceQueue<? super T> q)

2.不管其指向对象有没有强引用指向,get()的返回结果都是null。

android常见的内存泄漏

内存泄漏中最严重的当属Activity对象,一般来说一个Activity占用着很多资源,也就是很多内存空间,如果不在使用时,不能进行回收的话就有严重的内存泄漏了。Activity内存泄漏的核心就是在生命周期外,仍然持有此Activity的引用。下面讲一下常见的内存泄漏的栗子和解决方法:

static activity

private static MainActivity activity;

void setStaticActivity() {
    activity = this;
}

static变量是贯穿整个应用的生命周期的,被泄漏的activity会一直存在于应用的进程中,不会被垃圾回收器回收。

解决:这种写法是有理由来使用的,为了避免内存泄漏,我们需要正确释放引用,让垃圾回收器在它销毁的时候对它进行回收。我们可以控制引用的“强度”,Activity对象泄漏是由于需要销毁时,仍然有强引用的指向,只要有强引用存在就无法被回收。弱引用不会阻止对象的内存释放,

 private static WeakReference<MainActivity> activityReference;

    void setStaticActivity() {
        activityReference = new WeakReference<MainActivity>(this);
    }

static view

private static View view;

void setStaticView() {
    view = findViewById(R.id.sv_button);
}

由于view持有其宿主Activity的引用,导致的内存泄漏问题也是一样的严重,

解决:对于这种泄漏,弱引用是一种有效的解决方式,同时也看可以在Activity生命周期结束时清除引用,

private static View view;

@Override
public void onDestroy() {
    super.onDestroy();
    if (view != null) {
        view = null;
    }
}

持有内部类的成员变量

private static Object inner;

void createInnerClass() {
    class InnerClass {
    }
    inner = new InnerClass();
}

非静态内部类会持有外部类的引用,inner持有外部类的引用,又被static修饰,这样的成员变量非常容易导致内存泄漏,static贯穿整个应用的生命周期,假如外部类生命周期结束了,但是inner被static修饰,与应用的生命周期一致,这部分内存在应用结束前就不能被释放再利用了,造成内存泄漏。

解决:下面持有内部类的成员变量是可以的

private Object inner;

void createInnerClass() {
    class InnerClass {
    }
    inner = new InnerClass();
}

匿名内部类

AsyncTask

void startAsyncTask() {
    new AsyncTask<Void, Void, Void>() {
        @Override protected Void doInBackground(Void... params) {
            while(true);
        }
    }.execute();
}

Handler

void createHandler() {
    new Handler() {
        @Override public void handleMessage(Message message) {
            super.handleMessage(message);
        }
    }.postDelayed(new Runnable() {
        @Override public void run() {
            while(true);
        }
    }, Long.MAX_VALUE >> 1);
}

Thread

void scheduleTimer() {
    new Timer().schedule(new TimerTask() {
        @Override
        public void run() {
            while(true);
        }
    }, Long.MAX_VALUE >> 1);
}

以上的匿名内部类都有可能导致内存泄漏。这些类会产生后台线程,Java中的线程是全局的,也就是这些线程会持有匿名内部类的引用,而匿名内部类又持有外部类的引用,线程如果长时间运行的话,一直持有Activity的引用,当Activity销毁时,仍然持有Activity的引用,直到线程结束,这样就导致了内存泄漏。

解决:静态内部类不会持有外部类的引用,使用静态内部类打破链式引用

AsyncTask

private static class NimbleTask extends AsyncTask<Void, Void, Void> {
    @Override protected Void doInBackground(Void... params) {
        while(true);
    }
}

void startAsyncTask() {
    new NimbleTask().execute();
}

Handler

private static class NimbleHandler extends Handler {
    @Override public void handleMessage(Message message) {
        super.handleMessage(message);
    }
}

private static class NimbleRunnable implements Runnable {
    @Override public void run() {
        while(true);
    }
}

void createHandler() {
    new NimbleHandler().postDelayed(new NimbleRunnable(), Long.MAX_VALUE >> 1);
}

Thread

private static class NimbleTimerTask extends TimerTask {
    @Override public void run() {
        while(true);
    }
}

void scheduleTimer() {
    new Timer().schedule(new NimbleTimerTask(), Long.MAX_VALUE >> 1);
}

Android系统服务使用不当

void registerListener() {
    SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
    sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}

我们在使用Android系统服务时,会将Activity作为监听器,这样的话引用链在传递事件和回调中形成了,只要Activity维持注册监听状态,引用就会一直存在,内存就不会被释放了。

解决:在Activity生命周期结束时注销监听器

private SensorManager sensorManager;
private Sensor sensor;

@Override
public void onDestroy() {
    super.onDestroy();
    if (sensor != null) {
        unregisterListener();
    }
}

void unregisterListener() {
    sensorManager.unregisterListener(this, sensor);
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值