moxt

Just another Blog site

LeakCanaryの仕組みをある程度理解したいマン

   

LeakCanaryとは何か

https://corner.squareup.com/2015/05/leak-canary.html

Square社が開発したメモリリーク検知ライブラリ。
導入はとてもシンプル。

これだけでメモリリークが発生したときに通知してくれる。便利。

では、メモリリークの検知をどのように実現しているのだろうか。
気になったので読んでみた。

どのタイミングでメモリリークの有無の確認を実行しているのか?

カギはActivityRefWatcherにあった。

https://github.com/square/leakcanary/blob/master/leakcanary-android/src/main/java/com/squareup/leakcanary/ActivityRefWatcher.java

ActivityLifecycleCallbacksをApplicationに登録することでアプリ内に登録されてるActivityのonXXXをListenできるようになる。

↑を見てみると、Activityが破棄されたタイミングでRefWatcherクラスのwatchメソッドが呼ばれている。

画面が破棄されたタイミングでメモリリークが無いかwatchして、メモリリークが確認できれば通知する。
っていう流れだろうか。

詳しく見てみる。

RefWatcherクラスの役割

https://github.com/square/leakcanary/blob/master/leakcanary-watcher/src/main/java/com/squareup/leakcanary/RefWatcher.java

watch

watchではどんな処理が実行されているのだろうか。

ensureGoneメソッドで与えられた参照がGCによって葬り去れるか調べてる感じ。
メインスレッドで実行するとブロッキングが発生するのでwatchExecutorが専用のThread上でensureGoneしてる。

KeyedWeakReferenceクラスが活躍するのはこれから。
このクラスのおかげで与えられた参照がGCされたかどうか検知できるようになる。
その辺は後ほど。

とりいそぎ、ensureGoneメソッドを見る。

ensureGone

ざっくりと流れは

  • GCを実行する
  • removeWeaklyReachableReferences()を実行して、与えられた参照がGCによって回収されているか確認しつつ、参照のキーを保持してるSetクラス変数retainedKeysからキーを抹消する(メソッド名と実際の処理内容のマッチングが微妙な感じではあるが。。)
  • retainedKeysに該当するキーが抹消されてるか確認して、残っていればGCで回収できない=誰かに参照されてる=メモリリークと判断する
  • メモリリークと判断されたらヒープをダンプして通知UIやら詳細情報表示する用Activityを表示する処理を実行する

と、こんな感じ。
上記の処理をあるActivityがDestroyされるたびに実行してる。
なんとなく流れは分かった気分になってる。

removeWeaklyReachableReferencesでKeyedWeakReferenceクラスが活躍する。

https://github.com/square/leakcanary/blob/master/leakcanary-watcher/src/main/java/com/squareup/leakcanary/KeyedWeakReference.java

↑の実装を見つつ、こちらの記事を見ると大変分かりやすい。

http://www.ne.jp/asahi/hishidama/home/tech/java/weak.html

複数のWeakReferenceを使う場合、どれがクリアされたかを知るには、全WeakReferenceをリストか何かに入れておいて一つ一つget()してnullが返ってくるかどうかをチェックすればいいような気がする。
しかしこれは無駄が多い。

そこで、もう少しエレガントな方法が用意されている。
参照キュー(ReferenceQueueクラス)というものを用意し、WeakReferenceのコンストラクターに渡す。
すると、WeakReferenceから値が消されると、参照キューにそのWeakReferenceが追加される。

これを踏まえて、再びremoveWeaklyReachableReferencesを見てみる

WeakReferenceを作るときにReferenceQueueを渡しておくと、GCが実行されたときにReferenceQueueに参照(ここでいうとKeyedWeakReference)が格納される。
で、queueのpollメソッドを呼び出すとqueueに詰まってる参照がとりだせる。
とりだせない、ということは参照先のオブジェクトに対してGCができなかった。ということ。

別のオブジェクトが参照先のオブジェクトを参照している=メモリリーク、なので集合retainedKeysからキーを取り除けないので、goneでfalseを返す。で、メモリリーク時の処理が実行される。

だいたいこんな感じ。

 - Android

  • このエントリーをはてなブックマークに追加
  • follow us in feedly

  関連記事

wallpaper.big-j.cs
Androidアプリで使う画像素材の圧縮をgulpで一括処理する

まず、デザイナーやらディレクターやらプロデューサーやらに『圧縮された画像をgitなどのバージョン管理システム上にコミットしてもらえる』そんな幸福な環境であれば無縁な話。 大概の組織では共有ファイルシステムに新しく追加する、もしくは変更された画像をドカッと置かれて「よろしく」って感じだろう。 …

no image
AndroidでGoogleTagManagerを使いたい

Contents1 GoogleTagManagerとは2 …

wallpaper.big-j.cs
GridViewからRecyclerViewに移行したい

GridViewからRecyclerViewにクラス名変えるだけじゃ動かない。 移行過程をメモ。 …

wallpaper.big-j.cs
手を動かしてViewDragHelperを学ぶ(無駄にKotlin)

まず、Youtubeの公式Androidアプリみたいなヤツ作りたくなった。 すでに有志がライブラリ作ってた。。 …

no image
[WIP]AndroidでParseを使ってみる

今更感溢れてるが。 バックエンドな実装・運用は面倒くさい、Railsとか使い方分からない、的な人はParse良いかもしれない。 …

wallpaper.big-j.cs
Facebook製の画像ライブラリFrescoのコードを読んでいる

Facebookが新たに画像ライブラリFrescoを公開した。 DraweeView,DraweeController,DraweeHierarchyというクラスを利用したMVC的な構成を成しており、画像を効率よく読み込むようになっているらしい。(雑) …

logo_og
ReactNativeでGiphyのデータを表示する

まずは下記をサクッとパクってみる。 当方、比較的AndroiderなのでAndroidで。 …

wallpaper.big-j.cs
Androidで超シンプルなWidgetを作りたい

情報が少なくてちょっとつまづいたのでメモ。 Contents1 …