android 内存泄漏详解

来源:互联网 发布:淘宝开店认证点击不了 编辑:程序博客网 时间:2024/06/05 15:19

一款音乐播放器,基于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;@Overridepublic 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;@Overridepublic void onDestroy() {    super.onDestroy();    if (sensor != null) {        unregisterListener();    }}void unregisterListener() {    sensorManager.unregisterListener(this, sensor);}
2 0
原创粉丝点击