2013-06-19 4 views
16

私のアプリでメモリリークの捜索で私は理解できない振る舞いを追いかけました。私は大きなメモリブロックを割り当てますが、onDestroyの参照を明示的にnullにしない限り、OOMの結果としてガベージコレクションされません。大きなメモリチャンクはガベージコレクトされていません

この例では、2つのほぼ同じアクティビティがあり、お互いに切り替わります。両方とも1つのボタンがあります。 MainActivityボタンを押すとOOMActivityが開始され、finish()を呼び出してOOMActivityが戻ります。ボタンを数回押すと、AndroidはOOMExceptionをスローします。

OOMActivityにonDestroyを追加してメモリチャンクへの参照を明示的にnullにすると、メモリ内のメモリが正しく解放されていることがログに記録されます。

ヌルなしでメモリが自動的に解放されないのはなぜですか?

MainActivity:

package com.example.oom; 

import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 

public class MainActivity extends Activity implements OnClickListener { 

    private int buttonId; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     System.gc(); 
     Button OOMButton = new Button(this); 
     OOMButton.setText("OOM"); 
     buttonId = OOMButton.getId(); 

     setContentView(OOMButton); 
     OOMButton.setOnClickListener(this); 
    } 

    @Override 
    public void onClick(View v) { 
     if (v.getId() == buttonId) { 
      Intent leakIntent = new Intent(this, OOMActivity.class); 
      startActivity(leakIntent); 
     } 
    } 

} 

OOMActivity:アクティビティ破壊がこのケースでのAndroid(あなたはクラスがOSを意味するものではありません表示されていないという理由だけで、クラスの破壊を意味するものではありません

public class OOMActivity extends Activity implements OnClickListener { 

    private static final int WASTE_SIZE = 20000000; 
    private byte[] waste; 
    private int buttonId; 

    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     Button BackButton = new Button(this); 
     BackButton.setText("Back"); 
     buttonId = BackButton.getId(); 

     setContentView(BackButton); 
     BackButton.setOnClickListener(this); 

     waste = new byte[WASTE_SIZE]; 

    } 

    public void onClick(View view) { 
     if (view.getId() == buttonId) { 
      finish(); 
     } 
    } 

} 
+0

素晴らしい例です。 – WarrenFaith

+0

このチャットをお読みください。我々は[ここでそれについて議論しました](http://chat.stackoverflow.com/transcript/message/10084186#10084186) – Reno

+2

これは、ハニカム前のデバイスでこれをテストしている可能性があります。ポストハニカムのガベージコレクタは、 "無駄な"オブジェクトを解放するほど攻撃的です。 4.1.2,4.2.2,2.3.5,2.3.7でテスト済み。 "waste = new byte [WASTE_SIZE]"の前にSystem.gc()を呼び出すと、ハニカム前のデバイスの問題を回避できます。 – pellucide

答えて

1

)それへのすべての参照を失い、それを終了します。そのため、ドキュメントでも、メモリリークを防ぐ必要がなくなったハンドルやオブジェクトを削除することが指定されています。乾杯。

1

いくつかのこと:

1)あなただけのGCログを見て、あなたの活動がリークされているか否かを判断することはできません。各JVM実装は、オブジェクトを参照するものがない場合でも、いつオブジェクトをガベージコレクトするかを自由に選択できます。それはOOMエラーをスローする前にガベージコレクトする必要があることに注意してください...しかし、十分なメモリがある場合は、あなたのアクティビティの10をメモリに保存し、それらをまとめて収集することを選択するかもしれません。

2)アクティビティのライフサイクルより長い間あなたのアクティビティへの参照を保持する内部のAndroid構造が存在する可能性があります。開発者はそれを制御できません。そのため、アクティビティが大量のデータを参照しないようにすることをお勧めします(そうした場合は、onDestroy(または、より積極的にしたい場合はonPause)で明示的にそれらの参照を解放する必要があります)。

3)一部のJVMは、実行時に最適化されるため、書き込まれたりアクセスされたりしないメモリのチャンクが実際に物理メモリに割り当てられることはありません。そのため、あなたのテストはAndroidの新しいバージョンでは有効ではありません。この問題を回避するには、配列内のいくつかの値をランダムな値に設定するループを追加し、それを読み取るコード内の別のループを追加することができます。このようにして、JVMは強制的にメモリを割り当てます。

関連する問題