Javaは、ごみ収集言語の一種です。その利点は、開発者が意図的にメモリの割り当てを管理する必要がないことです。これにより、現地のセグメンテーション障害によりアプリケーションがクラッシュする可能性が低下し、メモリのないメモリがスタック(HEAP)を絞るのを防ぎます。したがって、書かれたコードはより安全です。
残念ながら、Javaにはメモリ漏れが発生しやすい論理漏れがまだたくさんあります。注意しないと、Androidアプリケーションは簡単に不自由なメモリを簡単に無駄にする可能性があり、最終的にはメモリのエラーが発生します(Memory、OOM)。
1.一般的なメモリリークの理由は、オブジェクトへのすべての参照がリリースされた場合、オブジェクトがリリースされていないことです。 (翻訳者のメモ:カーソルは閉じるのを忘れました。)
2。論理メモリリークの理由は、アプリケーションがこのオブジェクトを必要としなくなった場合、オブジェクトへのすべての参照がリリースされていないことです。
オブジェクトへの強い参照を保持している場合、ゴミコレクターはメモリ内のオブジェクトをリサイクルできません。
Android開発では、最も可能性の高いメモリリークの問題はコンテキストです。たとえば、アクティビティのコンテキストには、ビュー階層やその他のリソースなど、多数のメモリ参照が含まれています。コンテキストがリークされると、それが指すすべてのオブジェクトを漏らすことも意味します。 Androidマシンのメモリは限られており、メモリの漏れが多すぎるとOOMが簡単につながる可能性があります。
論理メモリリークを検出するには、主観的な判断が必要です。特に、オブジェクトのライフサイクルは明確ではありません。幸いなことに、アクティビティには明確なライフサイクルがあり、リークの原因を簡単に見つけることができます。 Activity.ondestroy()は、アクティビティライフの終わりと見なされます。プログラムでは、破壊する必要があります。または、Androidシステムがこのメモリをリサイクルする必要があります(翻訳者の注:メモリが不十分な場合、Androidは目に見えないアクティビティをリサイクルします)。
この方法が実行されている場合、スタック内のアクティビティへの強い参照がまだあり、ガベージコレクターはそれをリサイクルされたメモリとしてマークすることができず、当初の目的はリサイクルすることです!
その結果、アクティビティはライフサイクル以外で生き残ります。
アクティビティはヘビー級のオブジェクトであり、Androidシステムで処理する必要があります。ただし、論理メモリリークは常に不注意に発生します。 (翻訳者のメモ:私はかつて20mのメモリリークを引き起こしたアクティビティを試しました)。 Androidでは、潜在的なメモリリークにつながるトラップが2つしかありません。
グローバルプロセスの静的変数(Process-Global)。アプリケーションの状態を無視し、アクティビティへの強力な参照を保持するこのモンスター。
アクティビティライフサイクルの外側に住むスレッド。活動への強い言及はクリアされていません。
次の状況に遭遇したかどうかを確認してください。
1. static活動
静的アクティビティ変数はクラスで定義され、現在実行中のアクティビティインスタンスはこの静的変数に割り当てられます。
この静的変数がアクティビティライフサイクルの終了後にクリアされない場合、メモリリークが発生します。静的変数はこのアプリケーションのライフサイクルを介して実行されるため、漏れたアクティビティは常に申請プロセスに存在し、ガベージコレクターによって収集されません。
静的アクティビティアクティビティ; void setStaticActivity(){Activity = this;} view sabutton = findViewByid(r.id.sa_button); sabutton.setonclickListener(new view.onclickListener(){@Override public void onclick(View view v){setstaticActivity(); nextStaticaftivity();2. stat派の見解
同様の状況がシングルトンモードで発生する可能性があり、アクティビティがよく使用される場合は、メモリ内でインスタンスを保存することが実用的です。前述のように、アクティビティのライフサイクルを強制することは非常に危険で不要であり、とにかく行うことはできません。
特別なケース:ビューの初期化が多くのリソースを消費し、アクティビティライフサイクル中に変更されない場合、静的に変換してビューヒエラチーにロードできます。このようにして、アクティビティが破壊されると、リソースをリリースする必要があります。 (翻訳者のメモ:メモリはサンプルコードでリリースされません。この静的ビューをnullするだけですが、静的ビューメソッドを使用することはお勧めしません)
静的ビュー; void setStaticView(){view = findViewById(r.id.sv_button);} view svbutton = findviewbyid(r.id.sv_button); svbutton.setonclicklistener(new view.onclicklistener(){@override public void onclick() }});3.インナークラス
継続して、アクティビティに内部クラスがあると仮定して、そうすることで読みやすさとカプセル化を改善することができます。内部クラスを作成し、静的変数への参照を保持する場合、おめでとうございます、メモリリークはあなたからそれほど遠くありません(翻訳者注:破壊したときは空です、um)。
私的静的オブジェクト内; void createinnerclass(){class innerclass {} inner = new innerclass();} view icbutton = findViewByid(r.id.ic_button); icbutton.setonclickListener(new View.onclickListener(){@Override public void onclick(view v);内部クラスの利点の1つは、外部クラスにアクセスできることです。残念ながら、メモリリークの理由は、内部クラスが外部クラスインスタンスへの強い参照を保持していることです。
4.匿名クラス
同様に、匿名クラスは外部クラスへの参照も維持しています。そのため、アクティビティで匿名のasynctskを定義すると、メモリリークが簡単に発生します。非同期タスクがバックグラウンドで時間のかかるタスクを実行すると、アクティビティは残念ながら破壊されます(翻訳者の注:ユーザーエグジト、システムリサイクル)、Asynctaskが保持しているこのアクティビティインスタンスは、非同期タスクが完了するまでゴミコレクターによってリサイクルされません。
void startasynctask(){new asynctask <void、void、void>(){@override Protected void doinbackground(void ... params){while(true); }} .execute();} super.oncreate(savedinstancestate); setcontentView(r.layout.activity_main); view aicbutton = findViewbyid(r.id.at_button); startasynctask();5.ハンドラー
同様に、匿名Runnableを定義し、匿名のクラスハンドラーで実行します。実行可能な内部クラスは、外部クラスへの暗黙の参照を保持し、ハンドラーメッセージキューのメッセージQueueに渡されます。アクティビティインスタンスは、メッセージメッセージが処理されるまで破壊されず、メモリリークが発生します。
void createhandler(){new Handler(){@Override public void handlemessage(message message){super.handlemessage(message); }} .postdelayed(new runnable(){@override public void run(){while(true);}}、long.max_value >> 1);} view hbutton = findViewbyid(r.id.h_button); v){createhandler();6.threads
スレッドとティマタスクを通してメモリリークを再び表示します。
void spawnthread(){new shood(){@override public void run(){while(true); }} .start();} view tbutton = findViewbyId(r.id.t_button); tbutton.setonclicklistener()new view.onclicklistener(){@Override public void onclick(View v){spawnThread();};};});7.Timertask
匿名のクラスのインスタンスである限り、ワーカースレッドにあるかどうかに関係なく、アクティビティへの参照を保持し、メモリリークが発生します。
void scheduletimer(){new Timer()。スケジュール(new Timertask(){@Override public void run(){while(true);}}、long.max_value >> 1);} view ttbutton = findViewid(r.id.tt_button) @Override void onclick(view v){scheduletimer();8.センサーマネージャー
最後に、システムサービスはContext.getSystemService(int name)を介して取得できます。これらのサービスは、それぞれのプロセスで機能し、アプリケーションがバックグラウンドタスクとハードウェアの相互作用を処理するのに役立ちます。これらのサービスを使用する必要がある場合は、リスナーを登録できます。これにより、サービスはコンテキストへの参照を保持します。アクティビティが破壊されたときにこれらのリスナーがログアウトされない場合、メモリリークが発生します。
void RegisterListener(){sensormanager sensormanager =(sensormanager)getsystemService(sensor_service);センサーセンサー= sensormanager.getDefaultSensor(sensor.type_all); sensormanager.registerListener(this、sensor、sensormanager.sensor_delay_fastest);} view smbutton = findViewbyid(r.id.sm_button); smbutton.setonclicklistener(new View.Onclicklistener(){@override public void onclick() nextactivity();要約します
メモリの漏れにつながる可能性のある非常に多くの例を見たので、携帯電話のすべてのメモリを食べることができ、ガベージのコレクションと処理をより頻繁にし、最悪の場合でもOOMにつながります。ごみ収集操作は高価であり、目に見えるラグにつながる可能性があります。したがって、インスタンス化するときは保持されている参照チェーンに注意を払い、多くの場合、メモリリークチェックを実行します。