优化-内存优化

内存屏障(Memory Barrier)

内存泄漏

内存泄漏检查的工具:常见的Leakcanary

内存泄漏原因

  1. 长时间持有ActivityFragment对象导致的内存泄漏

    及时释放ActivityFrament对象

  2. 匿名内部类和非静态内部类导致的内存泄漏

    避免匿名内部类和非静态内部类

  3. WebView持有Activity对象导致的内存泄漏

    在使用WebView时,及时调用destroy方法

  4. 单例模式持有资源对象导致的内存泄漏

    在单例模式中避免长时间持有资源对象

  5. 资源未关闭导致的内存泄漏

    及时关闭资源对象

  6. 静态变量持有Context对象导致的内存泄漏

    避免静态变量持有Context对象

  7. Handler持有外部类引用导致的内存泄漏

    避免Handler持有外部类引用

  8. Bitmap占用大量内存导致的内存泄漏

    在使用Bitmap时,及时释放内存

  9. 单例持有大量数据导致的内存泄漏

    避免单例持有大量数据

不一定要用弱引用来避免内存泄露

WeakReference弱引用应该是修复内存泄露的最后手段。

  1. (非常基础的方式)可以使用回调来避免内存泄露(AsyncTask)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class MainActivity extends Activity {
    private MyAsyncTask task;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    task = new MyAsyncTask();
    task.setListener(createListener());
    task.execute();
    }

    @Override
    protected void onDestroy() {
    task.setListener(null);
    super.onDestroy();
    }

    private MyAsyncTask.Listener createListener() {
    return new MyAsyncTask.Listener() {
    @Override
    public void onSuccess(Object object) {
    // adapt contents
    }
    };
    }
    }

    其中AsyncTask

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class MyAsyncTask extends AsyncTask {
    private Listener listener;
    @Override
    protected Object doInBackground(Object[] params) {
    return doSomeStuff();
    }

    private Object doSomeStuff() {
    //do something to get result
    return new Object();
    }

    @Override
    protected void onPostExecute(Object object) {
    if (listener != null) {
    listener.onSuccess(object);
    }
    }

    public void setListener(Listener listener) {
    this.listener = listener;
    }

    interface Listener {
    void onSuccess(Object object);
    }
    }
  2. 使用RxJava实现简单的例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class MainActivity extends Activity {

    private Subscription subscription;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    subscription = Observable
    .fromCallable(new Callable<Object>() {
    @Override
    public Object call() throws Exception {
    return doSomeStuff();
    }
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<Object>() {
    @Override
    public void call(Object o) {
    // adapt contents
    }
    });
    }

    private Object doSomeStuff() {
    //do something to get result
    return new Object();
    }

    @Override
    protected void onDestroy() {
    subscription.unsubscribe();
    super.onDestroy();
    }
    }
  3. 避免内存泄露的一个很重要的守则是让内部类为静态类的。尤其是它们要做耗时的后台任务的时候。或者更好的方法是把这个类移到外面作为单独的类。

推荐两个Novoda的项目,是很好的学习资源

novoda/bonfire-firebase-sample

bonfire-firebase-sample – An app to discuss your favourite emojis. This is a sample app built with Firebase.github.com

novoda/spikes

spikes – Where ideas & concepts are born & incubatedgithub.com

内存抖动

概念:在循环中创建对象(比如onDraw中创建对象)内存达到临界值,会触发GC。频繁创建,频繁触发GC。从内存分析器(如AS–Run–Profiler(Memory Profiler))内看就是忽上忽下的波动,称作Memory Churn(内存抖动)。

内存抖动,可能会发展成内存泄漏

错误使用单例造成的内存泄露

单例持有Context对象。构造方法中this.mContext=context.getApplicationContext()赋值导致的

解决:改成this.mContext = context;

完整:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class LoginManager {
private static LoginManager mInstance;
private Context mContext;

private LoginManager(Context context) {
this.mContext = context;
//修改代码:this.mContext = context.getApplicationContext();
}

public static LoginManager getInstance(Context context) {
if (mInstance == null) {
synchronized (LoginManager.class) {
if (mInstance == null) {
mInstance = new LoginManager(context);
}
}
}
return mInstance;
}

public void dealData() {}
}

//调用(未改之前:若传入的Application对象则没有问题,若是activity对象就会有问题;更改后就没有问题了)
LoginManager.getInstance(this).dealData();

静态集合类引起内存泄漏

Handler使用不当造成内存泄漏

handler是工作线程和UI线程之间通讯的桥梁

OOM