2012-11-23 6 views
12

Androidライブ壁紙にメモリリークがあると思います。画面を回転させるたびに、収集されるメモリガベージの量は50kb増加し、元に戻ることはありません。私はそれが予定されている未来によって引き起こされるかもしれないと思うので、そうであるかどうかを見極めるシナリオを提示するつもりです。予定された将来にメモリリークが発生する可能性はありますか?

次のメンバーを持つクラス(Fooと呼ぶ)があるとします。

private ScheduledFuture<?> future; 
private final ScheduledExecutorService scheduler = Executors 
     .newSingleThreadScheduledExecutor(); 

private final Runnable runnable = new Runnable() { 
    public void run() { 
     // Do stuff 
    } 
}; 

そして今、あなたがスケジュールされた未来を設定

future = scheduler.scheduleAtFixedRate(runnable, delay, speed, 
       TimeUnit.MILLISECONDS); 

将来は、実行可能への参照を保持し、かつ実行可能な親のFooオブジェクトへの参照を保持しています。これが当てはまるかどうかはわかりませんが、プログラムの中にFooへの参照がない場合、予定されている未来があるためガベージコレクタはまだ収集できません。私はマルチスレッド化にはあまり適していないので、私が示したコードが、スケジュールされたタスクがオブジェクトよりも長く生きることを意味しているかどうかはわかりません。つまり、ガベージコレクションに終わることはありません。

このシナリオでFooがガベージコレクションされないようにならない場合は、簡単な説明でそのことを伝える必要があります。 Fooがガベージコレクションされないようにしたら、どうすれば修正できますか? future.cancel(true); future = null;をする必要がありますか? future = nullは不要ですか?

答えて

5
  • runのいずれかの方法は、同封のFooクラスに依存しているため、単独では使用できません。その場合、私はあなたのFooをどのようにgc'edして、エグゼキュータによって実行される "実行中"の実行可能ファイルを保持するかわかりません
  • またはrunメソッドは、ステートに依存しないあなたのFooクラスのそれを静的にすることができ、あなたが経験している問題を防ぐことができます。

Runnableで中断を処理していないようです。つまり、future.cancel(true)に電話をしても、Runnableは引き続き実行されます。これは、漏れの原因となる可能性があると判断したものです。

Runnableを「割り込みに優しい」ようにする方法はいくつかあります。 InterruptedExceptionをスローするメソッド(Thread.sleep()やブロッキングIOメソッドなど)を呼び出すと、将来がキャンセルされたときにInterruptedExceptionがスローされます。あなたは、その例外をキャッチし、クリーンアップする必要があるものをクリーンアップした後、速やかにrunメソッドを終了して、中断状態を復元することができます

public void run() { 
    while(true) { 
     try { 
      someOperationThatCanBeInterrupted(); 
     } catch (InterruptedException e) { 
      cleanup(); //close files, network connections etc. 
      Thread.currentThread().interrupt(); //restore interrupted status 
     } 
    } 
}  

あなたはどのような方法を呼び出さない場合は、標準のイディオムは次のとおりです。

public void run() { 
    while(!Thread.currentThread().isInterrupted()) { 
     doYourStuff(); 
    } 
    cleanup(); 
} 

その場合、その間の状態が定期的にチェックされるようにする必要があります。

に電話すると、Runnableを実行しているスレッドに割り込み信号が送信され、実行中のタスクが終了し、RunnableとFooインスタンスがGCに適格になります。

+0

私の実行メソッドは、囲むFooクラスに依存します。しかし、Fooオブジェクトへの参照を破棄する前に、実行可能ファイルがメモリリークを引き起こさないようにするために、私は何をしなければなりません。 'future.cancel(true)'で十分でしょうか?私はすでにそれをやっているし、メモリリークはまだそこにある。もちろん、これは実行可能ファイルがリークの原因ではないことを意味する可能性があります。 – gsingh2011

+0

@ gsingh2011 'future.cancel(true)'はあなたの未来をキャンセルすると確信していますか?言い換えると、あなたの実行可能ファイルは中断され、中断されたときにそのジョブを終了できますか? – assylias

+0

私はあなたの実行可能ファイルが中断することができないことを理解できないので、私はそれが中断できないと仮定します。私はこれをどのようにするべきですか? – gsingh2011

0

今後、実行可能ファイルへの参照が保持され、実行可能ファイルには、親Fooオブジェクトへの参照が保持されます( )。この場合、 の場合はわかりませんが、プログラム内に何もFooの参照 の参照を保持していないと、ガーベジコレクタはまだ収集できません。 予定されている未来があるためですか?

そのアプリケーションが閉じたときに、あなたがシャットダウンScheduledExecutorService schedulerをすべきであるので、あなたが頻繁に作成一時オブジェクトのFooいくつかの並べ替えをする悪い考え。したがって、Fooを疑似シングルトンにする必要があります。

ガベージコレクタはサイクルを認識しますので、Fooのエグゼキュータサービスをシャットダウンすると、ほとんどの場合メモリに問題はありません。

5

この質問は長らく返信されていますが、thisの記事を読んだ後、私は説明付きで新しい回答を投稿したいと考えました。

今後予定されているメモリリークが発生する可能性がありますか? --- YES通知しません一般的に

ScheduledFuture.cancel()またはFuture.cancel()そのExecutorそれがキャンセルされていて、それが実行のためにその時間が到着するまでキューにとどまります。シンプルな先物取引ではそれほど大きな問題ではありませんが、ScheduledFuturesでは大きな問題になる可能性があります。スケジュールされた遅延に応じて、数秒、数分、数時間、数日、数週間、数年またはほぼ無期限にそこにとどまることができます。

ここでは、最悪の場合のシナリオの例を示します。実行可能ファイルとそれが参照しているすべてのものが、Futureがキャンセルされた後でも、Long.MAX_VALUEミリ秒間Queueにとどまります。

public static void main(String[] args) { 
    ScheduledThreadPoolExecutor executor 
     = new ScheduledThreadPoolExecutor(1); 

    Runnable task = new Runnable() { 
     @Override 
     public void run() { 
      System.out.println("Hello World!"); 
     } 
    }; 

    ScheduledFuture future 
     = executor.schedule(task, 
      Long.MAX_VALUE, TimeUnit.MILLISECONDS); 

    future.cancel(true); 
} 

あなたはProfilerを使用して、またはその中に一つの要素(それはキャンセルしまったRunnableの)のリストを返しますScheduledThreadPoolExecutor.shutdownNow()メソッドを呼び出すことで、これを見ることができます。

この問題の解決方法は、将来の実装を記述するか、またはいつでもpurge()メソッドを呼び出すことです。カスタムExecutorファクトリソリューションの場合:

public static ScheduledThreadPoolExecutor createSingleScheduledExecutor() { 
    final ScheduledThreadPoolExecutor executor 
     = new ScheduledThreadPoolExecutor(1); 

    Runnable task = new Runnable() { 
     @Override 
     public void run() { 
      executor.purge(); 
     } 
    }; 

    executor.scheduleWithFixedDelay(task, 30L, 30L, TimeUnit.SECONDS); 

    return executor; 
} 
+1

このシナリオではどうなるでしょうか?これは、単一のスレッド実行プログラムです。 Runnableタスクが長時間実行タスク(1日と言えば)で、遅延と期間が上記と同じ場合。タスクはキューに入れられますか?タスクの待ち行列に上限がありますか? – cherryhitech

関連する問題