2015-09-04 13 views
7

メモリリークの特定に常に苦労しています。私は私のプロジェクトでいくつかのメモリリークを持っていると思います。circular progress view実際の例のメモリリーク

私の推測の1つは、内部クラスFadeRunnableにメモリリークがあることです。 しかし正直言って、これが正確に問題の原因であるかどうかを知る方法を正確には分かりません。さて、通常のシナリオを実行して向きを切り替えると、以下に示すようにメモリ使用量が増加します。私はFadeRunnableクラスの使用法をコメントアウトした場合、私は何かを参照して、ステップが小さくなっている(が、まだそこに、私はそれが唯一のリークはないと思う)私は、ヒープ・ダンプを分析すると

memory steps

。しかし、実際には値の意味が分かりません。私は物事は

  • オープンヒープダンプとソート「留保サイズ」私は「CircularProgressView」私は右のエリアに8行を参照してくださいをクリックして今すぐ
  • によって

    1. 変更オリエンテーション何倍である。私は推測しますこれは「CircularProgressView」の8つのインスタンスがリークしたとメモリのどこかに孤児としての生活があることを意味します。

    はこの正しいですか?もしそうなら、どのように私はダンプ情報に見つけることができます(私がどこかにあると思います下のペイン)で、このオブジェクトが保存/保持されます。

    heap dump

    私はかどうかを確認するために、どのオブジェクトがいくつかのメモリをリークしている方法をステップバイステップの説明を持っているのが大好きです。

    疑わしいビューのすべてのコードがこのクラスにあります。

    https://github.com/momentummodules/CircularProgressView/blob/master/circularprogressview/src/main/java/momentum/circularprogressview/CircularProgressView.java

    しかし、また、より深い洞察力と、あなたがそれで遊んしたい場合のための完全なプロジェクトをチェックアウトして自由に感じます。

    ありがとうございます!

    UPDATE

    は、上記のコードのリンクは、MEM-漏洩内部クラスの固定コードを示します。次のスニペットは、私はあなたがそこに正しい方向性を持っていると思うこと

    /** 
    * Mem-leaking code, for fixed code see repository link 
    * https://github.com/momentummodules/CircularProgressView/blob/master/circularprogressview/src/main/java/momentum/circularprogressview/CircularProgressView.java 
    */ 
    public class CircularProgressView extends View 
    { 
        ... 
        private Thread fadeThread = null; 
        ... 
    
        ... 
        class FadeRunnable implements Runnable 
        { 
         @Override 
         public void run() 
         { 
          ... 
         } 
        } 
        ... 
    
        ... 
        private void startFade(boolean fadeIn) 
        { 
         // check existing 
         if(this.fadeThread != null) 
         { 
          // check if fade is already running 
          switch(this.fadeThread.getState()) 
          { 
           case TERMINATED: 
           case NEW: 
            this.fadeThread = null; 
            break; 
           case RUNNABLE: 
           case BLOCKED: 
           case TIMED_WAITING: 
           case WAITING: 
            return; 
          } 
         } 
         // create new 
         this.fadeThread = new Thread(new FadeRunnable(fadeIn, this.fadeTime)); 
         this.fadeThread.start(); 
        } 
    } 
    
  • +1

    LeakCanaryライブラリ(https://github.com/square/leakcanary)からの起動をお勧めします。それはあなたが文脈を漏らした場合あなたに表示されるボックスの外に統合することは非常に簡単です。 –

    +0

    ええ、私の大きなプロジェクトには既にそれを使用してください。しかし、実際には、第三者のlibに頼るのではなく、すべてを理解することに興味があります! –

    答えて

    5

    はい、FadeRunnableクラスにメモリリークがあります。

    内部クラスの各インスタンスには、その外部クラスへの暗黙の参照が含まれており、OuterClass.this演算子からアクセスできます。あなたのプロジェクトで、FadeRunnableを実行して方向変更によって再構成をトリガすると、アクティビティ全体とCircularProgressViewが再作成されますが、前のFadeRunnableはまだ有効です(割り当てられています)。 CircularProgressViewクラスでも、ビューは継続して保存されます。そのため、いくつかの再構成の後で、メモリ内に8個のインスタンスCircularProgressViewが割り当てられ、それが悪化すると、すべてのビューがそのコンテキストへの参照を保持します。漏れ。

    囲みアクティビティ、フラグメント、ビューなどを超えることができるランナブル、ハンドラおよび同様のオブジェクトは、標準クラスまたはSTATIC内部クラスとして宣言する必要があります(静的内部クラスはその外部クラスへの暗黙的参照を保持しません) ContextViewなどのような参照を保持しないでください。は、Activityが設定変更によって再作成されたときに、Viewを破棄してガベージコレクタによって解放することができます。

    Thisは非常に有益な記事ですが、私は強くお勧めします。

    +0

    ありがとうございました。私はそのシナリオを持っています。しかし、一般的な段階的なメモリリーク解析のためのリンクがありますか?私はヒープダンプを使って 'FadeRunnable'が原因であることを知る方法に興味があります。 –

    +0

    私は今思うことはできません、ごめんなさい – maciekjanusz

    2

    のように使用すべきではありませんオリジナルのMEM-漏れるのコードを示しています。このFadeRunnableは確かにクールではありません。他のメモリリークがあっても、これをチェックする必要があります。

    一般に、ビューで実際に行うべきことはかなり異なります。特に、ビューにはスレッドを必要とせずにタイミングとアニメーションを処理する機能があります。

    私は、ビュー上のものをアニメーション化するためのよりシンプルでクリーンなアプローチだと思います。

    • 実行ファイルとスレッドを完全に削除します。その後、

    あなたがアニメーションを開始するには:

    ValueAnimator animation = ValueAnimator.ofFloat(0, 1); 
    animation.setDuration(500); 
    animation.addUpdateListener(animationUpdate); 
    animation.addListener(animationUpdate); 
    animation.start(); 
    

    、その後、あなたはそれらのリスナー

    // this gets called for every animation update, 
        // inside this call you update `CircularProgressView.this.fadeAlpha` 
        private final ValueAnimator.AnimatorUpdateListener animationUpdate = new ValueAnimator.AnimatorUpdateListener() { 
         @Override public void onAnimationUpdate(ValueAnimator animation) { 
         // this fraction varies between 0f and 1f 
         float fraction = animation.getAnimatedFraction(); 
         // ... do your calculation 
    
         ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); 
         } 
        }; 
    
        // this is an optional one only if you really need 
        // in that you get notified when the animation starts and ends 
        private final Animator.AnimatorListener animationListener = new AnimatorListenerAdapter() { 
    
        @Override public void onAnimationStart(Animator animation) { 
         // anything u need goes here 
         ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); 
        } 
    
         @Override public void onAnimationEnd(Animator animation) { 
         // anything u need goes here 
         ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); 
         } 
        }; 
    

    を必要とし、それはそれについてですが。

    実際のメモリリーク解析のトピックでは、今からリークカナリーライブラリを永遠に使用することをお勧めします。https://github.com/square/leakcanaryこれは、私たち(開発者)がメモリリークを追跡するのに役立つ素晴らしいツールです。

    編集:

    は、なぜあなたはこのアニメーションのメモリリークを持っていますか? それは非常に簡単です:スレッド新しいスレッドを作成しますstartFade(boolean);

    • と新しい実行可能
    • (それは非静的内部クラスだから)実行可能なビューへの参照を持っている
    • を持っていますRunnableへの参照、それを実行することができます。
    • フレームワークがUIの一部ではないため、ビューを破棄します(回転、戻るボタン)
    • スレッドはまだ実行されており、実行可能ファイルはループしていますが、Runnableが参照するため、Viewオブジェクトはまだ破棄されません。
    • ビューオブジェクトのインスタンスはContextであり、このコンテキストはActivityです。

    このシーケンスの最後に、あなたのアクティビティはGC、AKAによってガベージコレクションされません。メモリリーク!

    +0

    値をアニメーション化するためのこのアプローチに感謝します。しかし、メモリリークに関して、私はすでに他のプロジェクトでリークカナリアを使用していますが、全体的なメモリリークの話を最初から理解したいと思います。 –

    +1

    @martynmlostekk私の答えの最後に私の編集を見てください。 – Budius

    +0

    もう1つ質問です。 'postInvalidate'を使うことができますか?あるいは 'postInvalidate'と' postInvalidateOnAnimation'の違いは何ですか? –

    関連する問題