10

アプリケーションでメモリ リークが発生していることに気付きました。これは DDMS で確認でき、OutOfMemoryError を取得することができました。

漏れの元を見つけました。アクティビティの 1 つに、バックグラウンドで実行されているスレッドがあります。このスレッドは で停止していonDestroy()ます。DDMS で確認できるように、実行が終了します。

現在、スレッドが開始されると、リークが発生します。アクティビティは、スレッドによって参照されるため、破棄された後にガベージ コレクションされません。スレッドがまったく開始されていない場合は、すべて問題ありません。

これを示す簡単な例を次に示します。

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    volatile boolean finished = false;
    byte[] memoryEater = new byte[4 * 1024 * 1024];

    Thread thread = new Thread(new Runnable() {

        @Override
        public void run() {
            while (!finished) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            Log.d(getClass().getName(), "Thread finished");
        }
    });

    @Override
    protected void onDestroy() {
        super.onDestroy();
        finished = true;
    }

    public void startActivity(View view) {
        startActivity(new Intent(this, MainActivity.class));
    }

    public void startThread(View view) {
        thread.start();
    }
}

新しいアクティビティを開始するためのボタンを 1 つ、スレッドを開始するためのボタンを 1 つ追加します。新しい活動を開始します。戻った後、スレッドが開始されていない場合にのみメモリが消去されます。

この動作の原因は何ですか?

4

4 に答える 4

13

私はちょうどこれと同じ問題を理解しました。

Tomasz、あなたは正しい方向に進んでいます。DDMSにバグはなく、プログラムにメモリリークもありません。

本当に問題なのは、プログラムをDEBUGモード(Eclipseの下)で実行していることです。どういうわけか、AndroidがDEBUGモードで実行されている場合、run()メソッドが終了した後でも、スレッドはガベージコレクションされません。おそらくAndroidは、いくつかのデバッグ機能が機能するためにスレッドを保持する必要があると思います。

ただし、アプリケーションをRUNモード(まだEclipseの下)で実行すると、スレッドのガベージコレクションが実行されます。スレッドは完全に解放され、アクティビティは完全に解放されます。

于 2012-10-10T06:38:34.593 に答える
4

私は調査を続けましたが、私が見つけたものは本当に驚くべきものです。実際のメモリリークはないようです。アプリが DDMS でデバッグ モードになっている場合にのみ発生します。

DDMS は何らかの形でこれらの完成したトレッドへの参照を保持しているようで、それらが GC 処理されるのを防いでいます。電話を切断して再度接続すると、「リークされた」リソースがすべて解放されていることがわかります。

DDMS のバグのようです。

于 2012-07-27T06:25:40.430 に答える
2

スレッドが使用する匿名の実行可能クラスには、アクティビティ (「this」) への参照があります。スレッドはアクティビティによって参照され、スレッド内のランナブルはアクティビティを参照するため、GC はそれらのいずれも収集しません。

このようなことをもっとやってみてください:

private static RunnableClass implements Runnable
{
    @Override
    public void run() {
        while (!finished) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        Log.d(getClass().getName(), "Thread finished");
    }
});

Thread thread = new Thread(new RunnableClass());
于 2012-07-24T22:55:25.227 に答える
0

「戻る」が押されたとき、またはスタックの一番上から削除するために他のインテントが送信されたとき、アクティビティは破棄されません。Android OS のメモリが不足するまで、オーバーライドされた onDestroy() メソッドが呼び出されることはないと思います。ドキュメントから次のものを抽出できます。

アクティビティのライフサイクルに関する次のセクションで説明するように、アクティビティのライフサイクルは Android システムによって管理されるため、独自のアクティビティを完了する必要はありません。これらのメソッドを呼び出すと、予想されるユーザー エクスペリエンスに悪影響を与える可能性があるため、ユーザーがアクティビティのこのインスタンスに戻ることを絶対に望まない場合にのみ使用する必要があります。

通常、すべての Activity インスタンスは、最初の作成後にメモリ内にonStart()...onStop()配置され、破棄されることなくサイクルを通過します。Thread を実装onStop()して呼び出しfinish()MainActivityスレッドを解放し、その結果、ガベージ コレクションを行います。

更新: 上記のステートメントは正しくありません。質問で提供されたコードに基づいて、アクティビティを GC 処理しない理由はありません。

于 2012-07-24T23:38:28.103 に答える