web-dev-qa-db-ja.com

(可能性のある)について無知Androidメモリリーク

すべてのビットマップが適切にスケーリングされていることなどを確認した後でも、厄介なOutOfMemoryErrorsに直面しています。実際、この問題はビットマップにまったく関連していないようですが、間違っている可能性があります。

テストとエラー分離の目的で、ナビゲーションドロワー([戻る]ボタンを使用しない)を使用して、2つのアクティビティ(メインとリストと呼びます)を切り替えています。 DDMSで、戻るたびに割り当てられたメモリが約180KB増加することがわかります。

メモリダンプを実行し、EclipseMATを使用して3つの異なる時点を分析しました。

Screen1

Screen2

Screen3

メモリリークが疑われますが、原因を特定できません。メモリダンプによると、増加し続けているのは「残り」とJava.lang.FinalizerReferenceのようです。 この質問 のユーザーも、メモリダンプに多くのFinalizerReferencesを持っていますが、答えは明確ではありません。

前回作成したリーク容疑者レポートは、Android.content.res.ResourcesAndroid.graphics.Bitmapの疑いがあるため、あまり役に立ちません。これらは、時間の経過とともに増加していないようです。

Screen3LeakReport

レポートの1つ(残念ながら、ここにはありません)で、リークの疑いがあると指摘されたAndroid.widget.ListViewの13のインスタンスを見ました。

これらのメモリの増加は、アクティビティ間の遷移で発生します(この例で使用したメインとリストだけではありません)。

(自明ではない?)メモリリークを見つけるにはどうすればよいですか?私は長い間頭を悩ませてきたので、どんな助けやヒントも素晴らしいでしょう。

編集:

  • ビットマップ(@ OrhanC1):上記の2つのアクティビティでBitmapのインスタンス化についてコメントしましたが、メモリはまだ増えています。メモリダンプにはまだいくつかのビットマップが表示されますが、それらはリソースに関連しており、私が割り当てた実際のビットマップではないと思います。

  • カスタムフォント(@erakitin)について:私はそれらを使用していますが、シングルトンを使用して、Typefaceコンテキスト(public class MyApp extends Application)に各Applicationの単一インスタンスを保持しています。上記の2つのアクティビティでフォントへの参照をコメントしようとしましたが、メモリはまだ増えています。

  • Context(@ DigCamara)をリークしているとは思わない:これら2つのアクティビティ内に静的参照を保持していません。アダプターを除いて、Applicationの代わりにActivityコンテキストを使用しています。同じActivityにとどまり、画面を回転させても、メモリは増加しません。

  • @NickTのコメントに基づく:両方のアクティビティのインスタンスが多数あることがわかります。これらのメモリの増加は、バックスタックのアクティビティ数の増加の結果であり、メモリリークではない可能性があります(OSは処理しましたがそれ、 どうやらそうではない )? FLAG_ACTIVITY_REORDER_TO_FRONTインテントフラグを使用すると、すべての異なるアクティビティがインスタンス化されるまで(1回)メモリが増加するだけです。この問題に役立ちます: メモリが少ないときにAndroidがスタックからアクティビティを強制終了しない

24
user1987392

Remainderが増加している理由は、バックスタック内のActivityインスタンスの数の増加であるように見えます(たとえば、「リスト」の13インスタンスActivity + 13 「メイン」のインスタンスActivity)。

ユーザーが[ホーム]ボタンをクリックすると(アプリの[ダッシュボード]に移動します)、_Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK_フラグを設定するようにナビゲーションドロワーを変更しました。アクティビティは再利用され、バックスタックはクリアされました( Androidガイドライン、実際には で推奨されています)。実際、ダッシュボードの複数のインスタンスが必要ないため、これはすでに実行されているはずです( "ホーム」)作成されるアクティビティ。

これを行うことにより、アクティビティが破棄され、割り当てられたヒープサイズ(「Remaining」スライスを含む)が減少することがわかります。

Fixing problem with increasing memory.

これを修正しているときに、バックスタックがクリアされた(リークした)場合でも、アクティビティの1つとそれによって使用されるビットマップが破棄されていないことにも気付きました。 MATで分析した後、このサブ問題の原因は、ImageViewに保持しているActivityへの参照であると結論付けました。このコードをonStop()メソッドに追加することで、アクティビティとBitmapの両方を破棄することができました。

_@Override
protected void onStop() {
    super.onStop();

    ImageView myImage = (ImageView) findViewById(R.id.myImage );
    if(myImage .getDrawable() != null)
        myImage.getDrawable().setCallback(null);

    RoundedImageView roundImage = (RoundedImageView) findViewById(R.id.roundImage); // a custom View
    if(roundImage.getDrawable() != null)
        roundImage.getDrawable().setCallback(null);


}
_

次に、すべてのActivityFragmentActivityを一般化して、unbindDrawables(View view)onDestroy()を呼び出すようにしました。

_private void unbindDrawables(View view)
{
    if (view.getBackground() != null)
    {
        view.getBackground().setCallback(null);
    }
    if (view instanceof ViewGroup && !(view instanceof AdapterView))
    {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++)
        {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }
        ((ViewGroup) view).removeAllViews();
    }
}
_

私を正しい方向に向けてくれた@NickTに感謝します。

11
user1987392