Android common interview questions - the memory leak causes and solutions

Foreword

Interview the most frequently asked is: "Do you understand Android Android memory leaks and memory overflow reason for you, please briefly", then most people can give reasons and examples and solutions, but the actual project slightly do not pay attention or cause a memory leak, today to sort out those memory leaks are common wording and solutions.

the reason

Memory leak principle of many people understand, but in order to strengthen everyone's awareness to prevent memory leaks, for me again. When it comes to the principle of memory leaks have to talk about Java in the GC. The reason why he was so popular Java object-oriented programming is not just a way, there is another important reason is because it can help programmers work without having to release the memory, but Java did not we think of that intelligence, memory scrubbing it to We had to rely on a fixed judgment logic.

Java can be divided into the GC

Reference counting algorithm

Add a reference to an object counter, whenever a reference to its place, the counter value is incremented by 1; when referring to the failure, the counter value is decreased by 1; subject at any time the counter value of 0 is no longer being used , that can be recycled objects. This principle is easy to understand and efficient, but there is a fatal flaw is the problem can not be resolved between the object of a circular reference each other. As shown below

webp

Reachability analysis algorithm

A fatal problem for the reference counting algorithm, reachability analysis algorithm can easily solve this problem. Reachability algorithm from the GC root by traversing out, if the node is not traversed from the root node indicates that the node corresponding to the object is recoverable state, as shown in obj1, obj2, obj3, obj5 all be from the root node can be node arrive. In contrast obj4, obj6, obj7 but can not reach the root, even obj6, obj7 circular reference each other but still objects belonging recyclable last jvm clean up.

webp

Read this knowledge point, let us look for the cause of memory leaks, Android is a Java-based language that garbage collection is also based on Jvm established, so that the GC is also Android by reachability analysis algorithm determination. But if a long survival time shorter survival time object holds another object will lead to shorter survival time of the object has been identified in a timely manner can not be recovered up to what we often say that the memory leak in the GC. Android has a memory for use of each App strict limits, a large number of memory leaks can lead to OOM, that is, when the new object request space, there are no remaining heap memory allocation caused.

Now that you know what principles it usually will face this problem and how to solve this problem it reasonable. The following examples speak to the press.

webp

Examples of memory leaks

Handler

Handler comes to this thing, we usually certainly no less with this stuff, but not very prone to problems, if used. for example

public Handler handler = new Handler(){    @Override
    public void handleMessage(Message msg) {      super.handleMessage(msg);
      toast("handlerLeakcanary");
    }
  };private void handlerLeakcanary(){
    Message message = new Message();
    handler.sendMessageDelayed(message,TIME);
  }

Honestly certainly a lot of people wrote the code. Many of them understand the principles of human memory leaks. But usually they need more time to believe it may write this irritating code.

webp

了解Handler机制的人都明白,但message被Handler send出去的时候,会被加入的MessageQueue中,Looper会不停的从MessageQueue中取出Message并分发执行。但是如果Activity 销毁了,Handler发送的message没有执行完毕。那么Handler就不会被回收,但是由于非静态内部类默认持有外部类的引用。Handler可达,并持有Activity实例那么自然jvm就会错误的认为Activity可达不就行GC。这时我们的Activity就泄漏,Activity作为App的一个活动页面其所占有的内存是不容小视的。那么怎么才能合理的解决这个问题呢

1、使用弱引用

Java里面的引用分为四种类型强引用、软引用、弱引用、虚引用。如果有不明白的可以先去了解一下4种引用的区别

 public static class MyHandler extends Handler{
    WeakReference<ResolveLeakcanaryActivity> reference;    public MyHandler(WeakReference<ResolveLeakcanaryActivity> activity){
      reference = activity;
    }    @Override
    public void handleMessage(Message msg) {      super.handleMessage(msg);      if (reference.get()!=null){
        reference.get().toast("handleMessage");
      }
    }
  }

引用了弱引用就不会打扰到Activity的正常回收。但是在使用之前一定要记得判断弱引用中包含对象是否为空,如果为空则表明表明Activity被回收不再继续防止空指针异常

2、使用Handler.removeMessages();
知道原因就很好解决问题,Handler所导致的Activity内存泄漏正是因为Handler发送的Message任务没有完成,所以在onDestory中可以将handler中的message都移除掉,没有延时任务要处理,activity的生命周期就不会被延长,则可以正常销毁。

单例所导致的内存泄漏

在Android中单例模式中经常会需要Context对象进行初始化,如下简单的一段单例代码示例

public class MyHelper {  private static MyHelper myHelper;  private Context context;  private MyHelper(Context context){    this.context = context;
  }  public static synchronized MyHelper getInstance(Context context){    if (myHelper == null){
      myHelper = new MyHelper(context);
    }    return myHelper;
  }  public void doSomeThing(){

  }

}

这样的写法看起来好像没啥问题,但是一旦如下调用就会产生内存溢出

  public void singleInstanceLeakcanary(){
    MyHelper.getInstance(this).doSomeThing();
  }

首先单例中有一个static实例,实例持有Activity,但是static变量的生命周期是整个应用的生命周期,肯定是会比单个Activity的生命周期长的,所以,当Activity finish时,activity实例被static变量持有不能释放内存,导致内存泄漏。
解决办法:
1.使用getApplicationContext()

  private void singleInstanceResolve() {
    MyHelper.getInstance(getApplicationContext()).doSomeThing();
  }

2.改写单例写法,在Application里面进行初始化。

匿名内部类导致的异常

 /**
   * 匿名内部类泄漏包括Handler、Runnable、TimerTask、AsyncTask等
   */
  public void anonymousClassInstanceLeakcanary(){    new Thread(new Runnable() {      @Override
      public void run() {        try {
          Thread.sleep(TIME);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }).start();
  }

这个和Handler内部类导致的异常原理一样就不多说了。改为静态内部类+弱引用方式调用就行了。

静态变量引用内部类

  private static Object inner;  public void innearClassLeakcanary(){    class InnearClass{

    }
    inner = new InnearClass();
  }

因为静态对象引用了方法内部类,方法内部类也是持有Activity实例的,会导致Activity泄漏
解决方法就是通过在onDestory方法中置空static变量

网络请求回调接口

    Retrofit retrofit = new Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl("http://gank.io/api/data/")
        .build();
    Api mApi = retrofit.create(Api.class);
    Call<AndroidBean> androidBeanCall = mApi.getData(20,1);
    androidBeanCall.enqueue(new Callback<AndroidBean>() {      @Override
      public void onResponse(Call<AndroidBean> call, Response<AndroidBean> response) {
        toast("requestLeakcanary");
      }      @Override
      public void onFailure(Call<AndroidBean> call, Throwable t) {

      }
    });

这是一段很普通的请求代码,一般情况下Wifi请求很快就回调回来了,并不会导致什么问题,但是如果是在弱网情况下就会导致接口回来缓慢,这时用户很可能就会退出Activity不在等待,但是这时网络请求还未结束,回调接口为内部类依然会持有Activity的对象,这时Activity就内存泄漏的,并且如果是在Fragment中这样使用不仅会内存泄漏还可能会导致奔溃,之前在公司的时候就是写了一个Fragment,里面包含了四个网络请求,由于平时操作的时候在Wi-Fi情况下测试很难发现在这个问题,后面灰度的时候出现Crash,一查才之后当所附属的Activity已经finish了,但是网络请求未完成,首先是Fragment内存泄漏,然后调用getResource的时候返回为null导致异常。这类异常的原理和非静态内部类相同,所以可以通过static内部类+弱引用进行处理。由于本例是通过Retrofit进行,还可以在onDestory进行call.cancel进行取消任务,也可以避免内存泄漏。

RxJava异步任务

RxJava最近很火,用的人也多,经常拿来做网络请求和一些异步任务,但是由于RxJava的consumer或者是Observer是作为一个内部类来请求的时候,内存泄漏问题可能又随之而来

  @SuppressLint("CheckResult")  public void rxJavaLeakcanary(){
    AppModel.getData()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(        new Consumer<Object>() {          @Override
          public void accept(Object o) throws Exception {
            toast("rxJavaLeakcanary");
          }
        });
  }

这个代码很常见,但是consumer这个为内部类,如果异步任务没有完成Activity依然是存在泄漏的风险的。好在RxJava有取消订阅的方法可通过如下方法解决

  @Override
  protected void onDestroy() {    super.onDestroy();    if (disposable!=null && !disposable.isDisposed()){
      disposable.dispose();
    }
  }

Toast显示

看到这个可能有些人会惊讶,为啥Toast会导致内存泄漏,首先看一下

Toast.makeText(this,"toast",Toast.LENGTH_SHORT);

This code you are familiar with it, but if you do it could lead directly to a memory leak
, go here pass a Context, and Toast actually added a layout on the screen, which has a LinearLayout Toast, the Context is as LinearLayout initialization parameters, it would have been held Activity, Toast show we all know there is a time limit, in fact, is an asynchronous task, and finally allowed to disappear, but if you are still displayed in the Activity Toast is destroyed, because the display Toast not end not the end of the life cycle, this time Activity on a memory leak.
The solution is not to directly use the code, their own packages a ToastUtil, use ApplicationContext to call. Or to call by getApplicationContext, there is a to cancel the display by cancel toast variables

 private void toast(String msg){
    Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
  }

to sum up

Read so many are not feeling fact, the principle is very simple memory leaks, they change is really just a form of change, a new name. But pay no attention to these problems may still arise in the encoding. After I went to understand the principles of writing code

Guess you like

Origin blog.51cto.com/14217562/2407919