LeakCanary源码解析

内存泄漏

单例造成的内存泄漏

public class SingletonActivityContext {
    private static SingletonActivityContext instance;
    private Context context;

    private SingletonActivityContext(Context context) {
        this.context = context;
    }

    public static SingletonActivityContext getInstance(Context context) {
        if (instance == null) {
            instance = new SingletonActivityContext(context);
        }
        return instance;
    }
}
  • 问题: 如果传入的是Activity的context,当Activity退出时内存并不会被回收,因为该单例对象持有了Activity的引用。

  • 解决:传入Application的context,使单例的生命周期和应用程序的生命周期一样长。

非静态内部类创建静态实例造成的内存泄漏

public class StaticLeakActivity extends Activity {
    private static noneStaticClass mResource = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (mResource == null) {
            mResource = new noneStaticClass();
        }

    }

    private class noneStaticClass {

    }
}
  • 非静态内部类持有外部类引用,静态(static)实例mResource的生命周期和应用程序一样长,导致静态实例一直持有StaticLeakActivity的引用,导致StaticLeakActivity无法被内存回收。

  • 解决:把非静态内部类noneStaticClass改成静态内部类(加static修饰)。

Handler造成内存泄漏

public class HandlerLeakActivity extends Activity {
    private final Handler mLeakyHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {

        }
    };

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

        mLeakyHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
            }
        }, 1000 * 60 * 10);

        finish();
    }
    /*
    * 解决办法:
    * 1.将 Handler 声明为静态的
    * 2.通过弱引用的方式引入 Activity
    *
    * */
}
  • Message Queue -> message -> Handler -> Activity.

  • 解决:

    1. 将Handler声明为静态的,这样Handler的生命周期就和Activity无关了。
    2. 通过弱引用的方式引入Activity。

    或者

    1. onDestroy的时候调用Handler.removeCallbacksAndMessages();

Webview造成的内存泄漏

public class WebviewLeakActivity extends AppCompatActivity {
    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mWebView = (WebView) findViewById(R.id.wv_show);
        mWebView.loadUrl("http://www.baidu.com");
    }


    @Override
    protected void onDestroy() {
        destroyWebView();
        android.os.Process.killProcess(android.os.Process.myPid());
        super.onDestroy();
    }

    private void destroyWebView() {
        if (mWebView != null) {
            mWebView.pauseTimers();
            mWebView.removeAllViews();
            mWebView.destroy();
            mWebView = null;
        }
    }

}

解决: 单独进程

<activity
   android:name=".WebviewActivity"
   android:process="webview"/>

ReferenceQueue

  • 软引用 弱引用

  • 对象被垃圾回收,Java虚拟机就会把这个引用加入到与之关联的引用队列中。

源码解析

寻找内存泄漏

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Fresco.initialize(this);
        (1)跟进去
        LeakCanary.install(this);
    }
}
public static RefWatcher install(Application application) {
  return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
      .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())1)跟进去
      .buildAndInstall();
}
public RefWatcher buildAndInstall() {1)创建RefWatcher对象
  RefWatcher refWatcher = build();
  if (refWatcher != DISABLED) {2)开启内存泄漏弹窗的Activity。 
    LeakCanary.enableDisplayLeakActivity(context);3)通过ActivityRefWatcher监视Activity的内存泄漏。
    (4)跟进去  
    ActivityRefWatcher.install((Application) context, refWatcher);
  }
  return refWatcher;
}2)开启内存泄漏弹窗的Activity。 的底层调用。
public static void setEnabledBlocking(Context appContext, Class<?> componentClass,
    boolean enabled) {
  ComponentName component = new ComponentName(appContext, componentClass);
  PackageManager packageManager = appContext.getPackageManager();
  int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
  // Blocks on IPC.
  packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
}
public final class ActivityRefWatcher {

  /** @deprecated Use {@link #install(Application, RefWatcher)}. */
  @Deprecated
  public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
    install(application, refWatcher);
  }

  public static void install(Application application, RefWatcher refWatcher) {
    new ActivityRefWatcher(application, refWatcher).watchActivities();
  }1)activity生命周期回调
  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new Application.ActivityLifecycleCallbacks() {
        @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        }

        @Override public void onActivityStarted(Activity activity) {
        }

        @Override public void onActivityResumed(Activity activity) {
        }

        @Override public void onActivityPaused(Activity activity) {
        }

        @Override public void onActivityStopped(Activity activity) {
        }

        @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        @Override public void onActivityDestroyed(Activity activity) {4)重要
          ActivityRefWatcher.this.onActivityDestroyed(activity);
        }
      };

  private final Application application;
  private final RefWatcher refWatcher;

  /**
   * Constructs an {@link ActivityRefWatcher} that will make sure the activities are not leaking
   * after they have been destroyed.
   */
  public ActivityRefWatcher(Application application, RefWatcher refWatcher) {
    this.application = checkNotNull(application, "application");
    this.refWatcher = checkNotNull(refWatcher, "refWatcher");
  }

  void onActivityDestroyed(Activity activity) {6)跟进去
    refWatcher.watch(activity);
  }2)注册监听
  public void watchActivities() {
    // Make sure you don't get installed twice.
    stopWatchingActivities();
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
  }3)解除注册
  public void stopWatchingActivities() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
  }
}
public final class RefWatcher {

  public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
  (1)执行内存泄漏检测的
  private final WatchExecutor watchExecutor;2)查询是否在debug调试中,如果再调试中不会执行内存泄漏的检测
  private final DebuggerControl debuggerControl;3)触发GC垃圾回收
  private final GcTrigger gcTrigger;4)dump内存的堆文件
  private final HeapDumper heapDumper;5)持有待检测的和已经内存泄漏的对象的key
  private final Set<String> retainedKeys;6)创建WeakReference时传入的引用队列,主要是用来判断弱引用持有的对象是否被gc回收,gc回收后会把弱引用添加到队列中
  private final ReferenceQueue<Object> queue;7)监听产生heap文件的回调
  private final HeapDump.Listener heapdumpListener;8) 需要排除的一些系统内存泄漏
  private final ExcludedRefs excludedRefs;
}
public void watch(Object watchedReference) {
  watch(watchedReference, "");
}

/**
 * Watches the provided references and checks if it can be GCed. This method is non blocking,
 * the check is done on the {@link WatchExecutor} this {@link RefWatcher} has been constructed
 * with.
 *
 * @param referenceName An logical identifier for the watched object.
 */
public void watch(Object watchedReference, String referenceName) {
  if (this == DISABLED) {
    return;
  }
  checkNotNull(watchedReference, "watchedReference");
  checkNotNull(referenceName, "referenceName");
  final long watchStartNanoTime = System.nanoTime();1)添加唯一的key值
  String key = UUID.randomUUID().toString();
  retainedKeys.add(key);2)创建弱引用对象
  final KeyedWeakReference reference =
      new KeyedWeakReference(watchedReference, key, referenceName, queue);3)检测对象是否回收
  ensureGoneAsync(watchStartNanoTime, reference);
}
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {1)看下watchExecutor实现
  watchExecutor.execute(new Retryable() {
    @Override public Retryable.Result run() {2)跟进去
      return ensureGone(reference, watchStartNanoTime);
    }
  });
}

@Override public void execute(Retryable retryable) {3)通过idleHandler执行检测
  if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
    waitForIdle(retryable, 0);
  } else {
    postWaitForIdle(retryable, 0);
  }
}

void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
  mainHandler.post(new Runnable() {
    @Override public void run() {
      waitForIdle(retryable, failedAttempts);
    }
  });
}

void waitForIdle(final Retryable retryable, final int failedAttempts) {
  // This needs to be called from the main thread.
  Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    @Override public boolean queueIdle() {
      postToBackgroundWithDelay(retryable, failedAttempts);
      return false;
    }
  });
}
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
  long gcStartNanoTime = System.nanoTime();
  long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);1)删除已经被回收的弱引用 
  removeWeaklyReachableReferences();2)debug调试中返回
  if (debuggerControl.isDebuggerAttached()) {
    // The debugger can create false leaks.
    return RETRY;
  }3)已经回收就返回
  if (gone(reference)) {
    return DONE;
  }4)主动触发GC
  gcTrigger.runGc();5)删除已经回收的弱引用
  removeWeaklyReachableReferences();6)还没被回收就开始分析
  if (!gone(reference)) {
    long startDumpHeap = System.nanoTime();
    long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

    (7)获取dump快照
    File heapDumpFile = heapDumper.dumpHeap();
    if (heapDumpFile == RETRY_LATER) {
      // Could not dump the heap.
      return RETRY;
    }
    long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);8)开始分析
    heapdumpListener.analyze(
        new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
            gcDurationMs, heapDumpDurationMs));
  }
  return DONE;
}

总结:

  1. 通过Application.ActivityLifecycleCallbacks监听Activity的onDestroy方法。

  2. 当Activity调用onDestroy方法的时候,创建一个弱引用与Activity进行关联。并赋值一个唯一的Key。

  3. 当Activity被GC的时候,弱引用会被添加到ReferenceQueue。

  4. 没有添加到队列,手动触发GC,再次检测是否添加到队列。

  5. 没有,则发生内存泄漏。

分析内存泄漏

public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {
  long analysisStartNanoTime = System.nanoTime();

  if (!heapDumpFile.exists()) {
    Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
    return failure(exception, since(analysisStartNanoTime));
  }

  try {
    HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
    HprofParser parser = new HprofParser(buffer);1)接续Hprof文件转换成Snapshot快照
    Snapshot snapshot = parser.parse();2)去除重复的内存泄漏
    deduplicateGcRoots(snapshot);3)根据key查询解析结果是否有 我们需要的对象
    Instance leakingRef = findLeakingReference(referenceKey, snapshot);

    // False alarm, weak reference was cleared in between key check and heap dump.
    if (leakingRef == null) {4)在heap dump过程中引用被回收
      return noLeak(since(analysisStartNanoTime));
    }5)泄漏的路径
    return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
  } catch (Throwable e) {
    return failure(e, since(analysisStartNanoTime));
  }
}
private Instance findLeakingReference(String key, Snapshot snapshot) {
  (1)从内存快照找查找第一个泄漏的弱引用对象
  ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
  List<String> keysFound = new ArrayList<>();2)遍历这个对象的所有实例
  for (Instance instance : refClass.getInstancesList()) {
    List<ClassInstance.FieldValue> values = classInstanceValues(instance);
    String keyCandidate = asString(fieldValue(values, "key"));3) 如果key值和最开始定义的key值相同,那么返回这个泄漏对象
    if (keyCandidate.equals(key)) {
      return fieldValue(values, "referent");
    }
    keysFound.add(keyCandidate);
  }
  throw new IllegalStateException(
      "Could not find weak reference with key " + key + " in " + keysFound);
}
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
    Instance leakingRef) {

  ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
  ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);

  // False alarm, no strong reference path to GC Roots.
  if (result.leakingNode == null) {
    return noLeak(since(analysisStartNanoTime));
  }

  LeakTrace leakTrace = buildLeakTrace(result.leakingNode);

  String className = leakingRef.getClassObj().getClassName();

  // Side effect: computes retained size.
  snapshot.computeDominators();

  Instance leakingInstance = result.leakingNode.instance;

  long retainedSize = leakingInstance.getTotalRetainedSize();

  // TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
  if (SDK_INT <= N_MR1) {
    retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
  }

  return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
      since(analysisStartNanoTime));
}

总结:

  1. 在Snapshot中找到弱引用

  2. 遍历KeyedWeakReference这个类的所有实例

  3. 如果key值和最开始定义的key值相同,那么返回这个泄漏对象

参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值