Androidメモリリークの概要
メモリ管理の目的は、開発中のアプリケーションのメモリリークを効果的に回避するのに役立つことです。誰もがメモリリークに精通しています。簡単に言えば、リリースすべきオブジェクトはリリースされておらず、特定のインスタンスによって保持されているが、GCをリサイクルできないように使用されなくなったことを意味します。最近、私は多くの関連文書と資料を読みました。私はそれらを要約して解決し、あなたと共有し、学ぶことを計画しており、また、将来のコーディング中にこれらの状況を回避し、アプリケーションの経験と品質を向上させる方法について警告を出すことを計画しています。
Java Memory Leaksの基本から始めて、特定の例を使用してAndroidのメモリ漏れのさまざまな原因を説明し、アプリケーションメモリリークを分析して最終的に要約するツールを使用する方法を説明します。
Javaメモリ割り当て戦略
Javaプログラムが実行されると、3種類のメモリ割り当て戦略、つまり静的割り当て、スタック割り当て、ヒープ割り当てがあります。それに対応して、3つのストレージ戦略で使用されるメモリスペースは、主に静的ストレージエリア(メソッドエリアとも呼ばれます)、スタックエリア、ヒープ領域です。
静的ストレージ領域(メソッド領域):主に静的データ、グローバルな静的データ、定数を保存します。このメモリは、プログラムがコンパイルされ、プログラムの実行中に存在するときに割り当てられます。
スタック領域:メソッドが実行されると、メソッドボディ(基本データ型とオブジェクト参照を含む)のローカル変数がスタックに作成され、これらのローカル変数が保持しているメモリはメソッド実行の最後に自動的にリリースされます。スタックメモリ割り当て操作はプロセッサの命令セットに組み込まれているため、非常に効率的ですが、割り当てられたメモリ容量は限られています。
ヒープ領域:動的メモリ割り当てとも呼ばれます。通常、プログラムが実行されているときに直接新しいメモリ、つまりオブジェクトのインスタンスを指します。メモリのこの部分が使用されていない場合、Java Garbage Collectorはリサイクルの責任を負います。
スタックとヒープの違い:
メソッドボディとオブジェクトの参照変数で定義されたいくつかの基本的なタイプの変数は、メソッドのスタックメモリに割り当てられます。変数がメソッドのブロックで定義されている場合、Javaはスタック上の変数にメモリスペースを割り当てます。変数の範囲を超えると、変数が無効になり、それに割り当てられたメモリ空間が解放され、メモリスペースが再利用されます。
ヒープメモリは、新しいオブジェクト(オブジェクト内のすべてのメンバー変数を含む)と配列によって作成されたすべてのオブジェクトを保存するために使用されます。ヒープに割り当てられたメモリは、Java Garbage Collectorによって自動的に管理されます。ヒープで配列またはオブジェクトが生成された後、スタックで特別な変数を定義できます。この変数の値は、ヒープメモリ内の配列またはオブジェクトの最初のアドレスに等しくなります。この特別な変数は、上記の参照変数です。この参照変数を介して、ヒープ内のオブジェクトまたは配列にアクセスできます。
例えば:
public class sample {int s1 = 0; sample msample1 = new sample(); public void method(){int s2 = 1; sample msample2 = new sample();}} sample3 = new sample();サンプルクラスのローカル変数S2と参照変数msample2はどちらもスタックに存在しますが、MSAMPLE2によって指されたオブジェクトはヒープに存在します。
Msample3によって指されたオブジェクトエンティティは、このオブジェクトのすべてのメンバー変数S1とMSAMPER1を含むヒープに保存され、スタックに存在します。
結論は:
ローカル変数の基本的なデータ型と参照はスタックに保存され、参照されるオブジェクトエンティティはヒープに保存されます。 - メソッドの変数に属しているため、ライフサイクルはメソッドで終わります。
メンバー変数はすべて保存され、ヒープ(基本的なデータ型、参照および参照オブジェクトエンティティを含む)にあります - クラスに属しているため、クラスオブジェクトは最終的に新しい使用に使用されます。
Javaの記憶割り当てを理解した後、Javaがどのようにメモリを管理するかを見てみましょう。
Javaがメモリを管理する方法
Javaのメモリ管理は、オブジェクトの割り当てとリリースの問題です。 Javaでは、プログラマーは新しいオブジェクトのメモリスペースを新機能(基本タイプを除く)を介して適用する必要があり、すべてのオブジェクトはヒープ(ヒープ)のスペースを割り当てます。さらに、オブジェクトのリリースはGCによって決定および実行されます。 Javaでは、メモリの割り当てはプログラムによって行われ、メモリリリースはGCによって行われます。この2行の収益と支出の方法は、プログラマーの作業を簡素化することです。しかし同時に、JVMの仕事にも追加されます。これは、Javaプログラムが遅くなる理由の1つでもあります。なぜなら、オブジェクトを正しくリリースするには、GCはオブジェクトのアプリケーション、引用、引用、割り当てなどを含む各オブジェクトの実行ステータスを監視する必要があり、GCはそれを監視する必要があるからです。
オブジェクトの状態を監視することは、オブジェクトをより正確かつタイムリーにリリースすることであり、オブジェクトを解放する基本原則は、オブジェクトがもはや参照されないことです。
GCの仕組みをよりよく理解するために、オブジェクトを指示されたグラフの頂点と見なし、参照されたオブジェクトを参照するオブジェクトを指すグラフの指示されたエッジとして参照関係を考慮することができます。さらに、各スレッドオブジェクトは、グラフの開始頂点として使用できます。たとえば、ほとんどのプログラムはメインプロセスから始まるため、グラフはメインプロセス頂点から始まるルートツリーです。この指示されたグラフでは、ルート頂点で到達可能なオブジェクトは有効なオブジェクトであり、GCはこれらのオブジェクトをリサイクルしません。オブジェクト(接続されたサブグラフ)がこのルート頂点から到達できない場合(グラフは指示されたグラフであることに注意してください)、この(それらの)オブジェクトはもはや参照されず、GCによってリサイクルできると考えています。
以下に、指示されたグラフを使用してメモリ管理を表す方法の例を示します。プログラムのすべての瞬間に、JVMのメモリ割り当てを表す指示グラフがあります。下の写真は、左のプログラムの図であり、6行6に走っています。
Javaは、メモリ管理に向けられたグラフを使用して、参照ループの問題を排除できます。たとえば、お互いを参照する3つのオブジェクトがあります。それらとルートプロセスが到達不可能である限り、GCはそれらをリサイクルすることもできます。この方法の利点は、メモリの管理において高い精度があるが、効率が低いことです。一般的に使用されるもう1つのメモリ管理技術は、カウンターを使用することです。たとえば、COMモデルはカウンターメソッドを使用してコンポーネントを管理します。指示されたグラフと比較して、精密なラインが低い(循環参照の問題に対処することは困難です)が、実行効率が高くなっています。
Javaのメモリリークとは何ですか
Javaでは、メモリリークは、次の2つの特性を持つ割り当てられたオブジェクトの存在です。まず、これらのオブジェクトは到達可能です。つまり、指示されたグラフには、それらに接続できるパスがあります。第二に、これらのオブジェクトは役に立たず、つまり、プログラムは将来これらのオブジェクトを再び使用しません。オブジェクトがこれらの2つの条件を満たしている場合、これらのオブジェクトはJavaのメモリリークであると判断でき、これらのオブジェクトはGCによってリサイクルされませんが、メモリを占有します。
C ++では、メモリリークの範囲が広いです。一部のオブジェクトはメモリスペースに割り当てられますが、到達できません。 C ++にはGCがないため、これらのメモリは収集されません。 Javaでは、これらの到達不可能なオブジェクトはGCによってリサイクルされるため、プログラマーはメモリリークのこの部分を考慮する必要はありません。
分析を通じて、C ++の場合、プログラマーは自分でエッジと頂点を管理する必要がありますが、Javaプログラマーの場合、エッジを管理する必要があります(頂点のリリースを管理する必要はありません)。このようにして、Javaはプログラミングの効率を向上させます。
したがって、上記の分析を通じて、Javaにはメモリリークもあることがわかりますが、スコープはC ++のスコープよりも小さいことがわかります。 Java言語は、あらゆるオブジェクトが到達可能であることを保証するため、すべての到達不可能なオブジェクトはGCによって管理されます。
プログラマーにとって、GCは基本的に透明で見えません。 Java言語仕様定義に従って、GCを実行するSystem.gc()など、GCにアクセスするためのいくつかの関数しかありませんが、この関数はJVMのゴミコレクターが実行されることを保証しません。なぜなら、異なるJVM実装者は、異なるアルゴリズムを使用してGCを管理する可能性があるためです。一般に、GCのスレッドは優先度が低くなります。 JVMがGCを呼び出すための多くの戦略があります。それらのいくつかは、メモリの使用量が特定のレベルに達したときにのみ動作し始めます。定期的に実行する人もいます。 GCをスムーズに実行する人もいれば、GCを割り込み方法で実行する人もいます。しかし、一般的に言えば、これを気にする必要はありません。いくつかの特定の状況でない限り、GCの実行はアプリケーションのパフォーマンスに影響します。たとえば、オンラインゲームなどのリアルタイムのWebベースのシステムの場合、ユーザーはGCがアプリケーションの実行を突然中断してガベージコレクションを実行することを望まないので、GCがガベージコレクションを分解して実行するための小さなステップに分解するなど、GCがスムーズにメモリを解放できるようにGCのパラメーターを調整する必要があります。 Sunが提供するホットスポットJVMは、この機能をサポートしています。
また、Javaメモリリークの典型的な例を示します。
Vector V = new Vector(10); }
この例では、オブジェクトオブジェクトサイクルを適用し、適用されたオブジェクトをベクトルに入れます。参照自体のみをリリースする場合、ベクトルはまだオブジェクトを参照するため、このオブジェクトはGCにリサイクルできません。したがって、オブジェクトをベクトルに追加した後にベクトルから削除する必要がある場合、最も簡単な方法は、ベクトルオブジェクトをnullに設定することです。
詳細なJavaのメモリリーク
1。Javaメモリリサイクルメカニズム
あらゆる言語のメモリ割り当て方法に関係なく、割り当てられたメモリの実際のアドレスを返す必要があります。つまり、メモリブロックの最初のアドレスへのポインターを返します。 Javaのオブジェクトは、新しいまたは反射方法を使用して作成されます。これらのオブジェクトの作成は、ヒープに割り当てられています。すべてのオブジェクトは、ゴミ収集メカニズムを介してJava仮想マシンによって収集されます。オブジェクトを正しくリリースするために、GCは各オブジェクトの健康状態を監視し、アプリケーション、引用、引用、割り当てなどを監視します。Javaは、指示されたグラフメソッドを使用してメモリを管理し、オブジェクトをリアルタイムで達成できるかどうかを監視します。到達しないと、リサイクルされ、参照ループの問題を排除できます。 Java言語では、メモリスペースがガベージコレクションの基準を満たしているかどうかを決定するメモリスペースには2つのタイプがあります。1つはオブジェクトに空の値を割り当てることです。これは以下に呼び出されません。もう1つはオブジェクトに新しい値を割り当て、したがってメモリスペースを再配置することです。
2。Javaメモリリークの原因
メモリリークとは、連続した役に立たないオブジェクト(使用されなくなったオブジェクト)を指します。または、役に立たないオブジェクトのメモリを時間内にリリースできないため、メモリ漏れと呼ばれるメモリスペースが無駄になります。メモリリークは深刻ではなく、検出するのが容易ではない場合があるため、開発者はメモリリークがあることを知りませんが、非常に深刻であり、メモリ外に促します。
Javaメモリリークの根本原因は何ですか? Long-Life Cycleオブジェクトが短命のサイクルオブジェクトへの参照を保持している場合、メモリリークが発生する可能性があります。短命のサイクルオブジェクトは不要ですが、長期的なサイクルの参照を保持するため、リサイクルすることはできません。これは、Javaでメモリリークが発生するシナリオです。主に次のカテゴリがあります。
1.静的コレクションクラスはメモリリークを引き起こします:
ハッシュマップ、ベクトルなどの使用は、メモリリークで発生する可能性が最も高くなります。これらの静的変数のライフサイクルは、アプリケーションのライフサイクルと一致しています。彼らが参照するすべてのオブジェクトは、ベクトルなども参照されるため、リリースできません。
例えば
静的ベクトルv = new Vector(10);
この例では、オブジェクトオブジェクトが適用されたループで、適用されたオブジェクトがベクトルに配置されます。参照自体がリリースされている場合(O = null)、ベクトルはまだオブジェクトを参照するため、このオブジェクトはGCでリサイクルできません。したがって、オブジェクトをベクトルに追加した後にベクトルから削除する必要がある場合、最も簡単な方法は、ベクトルオブジェクトをnullに設定することです。
2。コレクション内のオブジェクトプロパティが変更された場合、remove()メソッドは機能しません。
例えば:
public static void main(string [] args){set <surne> set = new Hashset <serson>(); Person P1 = new Person( "Tang Monk"、 "PWD1"、25); Person P2 = new Person( "Sun wukong"、 "pwd2"、26); perse p3 = new pers bajie "、" pwd3 "、27); set.add(p1); set.add(p2); set.add(p3); system.out.println("合計: "+set.size()+"要素! "); //結果:合計:3つの要素!P3.Setage(2); // P3の年齢を変更し、この時刻セットでP3要素の変更に対応するハッシュコード値を変更します。Remove(P3); //この時点でそれを削除し、メモリリークセットを引き起こします。Add(P3); //もう一度追加すると、system.out.println( "があります:"+set.size()+"elements!"); //結果:4つの要素があります! for(人の人:set){system.out.println(person);}}3。リスナー
Javaプログラミングでは、私たち全員がリスナーに対処する必要があります。通常、多くのリスナーがアプリケーションで使用されます。 addxxxlistener()などの制御方法を呼び出してリスナーを追加しますが、多くの場合、オブジェクトをリリースするときは、これらのリスナーを削除することを覚えていないため、メモリリークの可能性が高まります。
4。さまざまな接続
たとえば、データベース接続(dataSourse.getConnection())、ネットワーク接続(ソケット)、およびIO接続は、接続を閉じるためにclose()メソッドを明示的に呼び出さない限り、GCによって自動的にリサイクルされません。結果を明示的にリサイクルすることはできませんが、接続をいつでも自動的にリサイクルできないため、接続を明示的にリサイクルする必要があります。接続がリサイクルされると、結果セットとステートメントオブジェクトはすぐにヌルになります。ただし、接続プールを使用すると、状況は異なります。また、接続を明示的に閉じることに加えて、結果のステートメントオブジェクトを明示的に閉じる必要があります(そのうちの1つを閉じ、もう1つも閉じます)。そうしないと、多数のステートメントオブジェクトがリリースされず、メモリリークが発生します。この場合、接続は通常、TRYでリリースされ、最終的にリリースされます。
5。内部クラスおよび外部モジュールへの参照
内部クラスへの参照は比較的忘れがちであり、リリースされないと、一連の後継クラスオブジェクトがリリースされない場合があります。さらに、プログラマーは、外部モジュールへの不注意な参照にも注意する必要があります。たとえば、プログラマーAはモジュールAを担当し、次のようなモジュールBの方法を呼び出します。
public void Registermsg(オブジェクトB);
この種の電話には細心の注意が必要です。オブジェクトが渡されると、モジュールBがオブジェクトへの参照を保持する可能性が非常に高いです。現時点では、モジュールBが参照を削除するために対応する操作を提供するかどうかに注意する必要があります。
6。シングルトンモード
シングルトンパターンの誤った使用は、メモリリークを引き起こす一般的な問題です。 Singletonオブジェクトは、初期化後(静的変数の形式で)JVMのライフサイクル全体を通して存在します。 Singletonオブジェクトが外部参照を保持している場合、このオブジェクトはJVMによって正常にリサイクルされず、メモリリークが発生します。次の例を考えてみましょう。
class A {public a(){b.getinstance()。seta(this);} ....} // class bは、シングルトンモードクラスB {private a; private static b instance = new b(); public b(){} public static b getInstance(){return instance;} public void(a a =明らかに、BはオブジェクトAへの参照を保持するシングルトンパターンを採用し、このクラスAのオブジェクトはリサイクルされません。 Aがより複雑なオブジェクトまたはコレクションタイプであった場合はどうなるか想像してみてください
Androidの一般的なメモリリークの概要
コレクションクラスリーク
コレクションクラスには、要素を追加する方法のみがあり、対応する削除メカニズムがない場合、メモリは占有されます。このコレクションクラスがグローバル変数(クラス、グローバルマップなどの静的プロパティなど、つまり、常に静的な参照または最終的なポイントがあります)である場合、対応する削除メカニズムはありません。たとえば、上記の典型的な例は、これらの状況の1つです。もちろん、プロジェクトにこのような2Bコードを書くことは間違いありませんが、注意しないと簡単に起こります。たとえば、私たちは皆、ハッシュマップを介していくつかのキャッシュをするのが好きなので、この状況ではもっと注意する必要があります。
シングルトンによって引き起こされるメモリリーク
シングルトンの静的な性質は、アプリケーションのライフサイクルまでライフサイクルを行うため、不適切に使用すると、メモリの漏れを引き起こすのは簡単です。たとえば、次の典型的な例、
public class appmanager {private static appmanager instance; private context context; private appmanager(context context){this.context = context;} public static appmanager getInstance(context == null){if(instance = null){instance = new appmanager(context);} return instance;}}}}これは通常のシングルトンパターンです。このシングルトンを作成するとき、コンテキストを渡す必要があるため、このコンテキストのライフサイクルの長さは重要です。
1.アプリケーションのライフサイクルがアプリケーション全体のライフサイクルであるため、アプリケーションのコンテキストがこの時点で渡された場合、問題はありません。
2。この時点でアクティビティコンテキストが渡された場合、このコンテキストに対応するアクティビティが終了する場合、コンテキストへの参照はシングルトンオブジェクトによって保持されるため、ライフサイクルはアプリケーションのライフサイクル全体に等しく、アクティビティが終了すると、そのメモリがリサイクルされず、リークが発生します。
正しい方法を次のように変更する必要があります。
public class appmanager {private static appmanager instance; private context context; private appmanager(context context){this.context = Context.getApplicationContext(); // Applicationを使用したコンテキスト} public static appmanager getInstance(コンテキストコンテキスト){(instance == null){instance = new appmanager(context = new Appmanager(または、このように書くと、コンテキストを渡す必要さえありません。
アプリケーションに静的メソッドを追加すると、getContext()はアプリケーションのコンテキストを返します。
...
context = getApplicationContext(); .../*** Get Global Context*@return return Global Context Object*/public static Context getContext(){return Context;} public class appmanager {private static appmanager instance; private context; private appmanager(){this.context = myApplication.getContext(); (instance == null){instance = new appmanager();} return instance;}}匿名の内部クラス/非静的な内部クラスと非同期スレッド
非静的な内部クラスで静的インスタンスを作成することによって引き起こされるメモリリーク
時々、頻繁に活動を開始することがあります。同じデータリソースの作成を繰り返し避けるために、この書き方が発生する可能性があります。
パブリッククラスのMainActivityは、AppCompatactivityを拡張します{private Static testresource mresource = null; @overrideprotected void oncreate(bundle savedinstancestate){super.oncreate(savedinstancestate); setcontentView(r.layout.activity_main); testResource();} // ...} class testResource {// ...}}これにより、アクティビティ内の非静的な内部クラスのシングルトンが作成され、アクティビティが開始されるたびにシングルトンのデータが使用されます。リソースの繰り返しの作成は回避されますが、この書き込みはメモリリークを引き起こします。これは、非静的な内部クラスがデフォルトで外部クラスへの参照を保持し、非静的な内部クラスが静的なインスタンスを作成するため、インスタンスのライフサイクルはアプリケーションと同じくらい長くなります。それを行う正しい方法は次のとおりです。
内側のクラスを静的な内部クラスとして設定するか、内側のクラスを抽出し、それをシングルトンにカプセル化します。コンテキストを使用する必要がある場合は、上記の推奨コンテキストに従ってアプリケーションを使用してください。もちろん、アプリケーションのコンテキストは全能ではないため、ランダムに使用することはできません。一部の場所では、アクティビティのコンテキストを使用する必要があります。アプリケーション、サービス、およびアクティビティのコンテキストのアプリケーションシナリオは次のとおりです。
ここ:NO1とは、アプリケーションとサービスがアクティビティを開始できることを意味しますが、新しいタスクキューを作成する必要があります。ダイアログの場合、アクティビティでのみ作成できます
匿名の内部クラス
Android開発は、多くの場合、アクティビティ/フラグメント/ビューの実装を継承します。この時点で、匿名のクラスを使用し、非同期スレッドによって保持されている場合は、注意してください。測定がなければ、それは間違いなく漏れにつながります。
パブリッククラスのMainActivityはアクティビティを拡張します{... runnable Ref1 = new MyRunable(); runnable ref2 = new runnable(){@overridepublic void run(){}}; ...}ref1とref2の違いは、ref2が匿名の内部クラスを使用することです。実行時に参照されているメモリを見てみましょう。
ご覧のとおり、Ref1は特別なものではありません。
ただし、匿名クラスRef2の実装オブジェクトには追加の参照があります。
この$ 0の参照は、MainActivityを指します。つまり、現在のMainActivityインスタンスはREF2によって保持されます。この参照が非同期スレッドに渡され、このスレッドとこのアクティビティライフサイクルが矛盾している場合、アクティビティリークが発生します。
ハンドラーによって引き起こされるメモリリーク
ハンドラーの使用によって引き起こされるメモリリークの問題は、最も一般的であると言われるべきです。 ANRを回避するために、メインスレッドで時間のかかる操作を実行せず、ハンドラーを使用してネットワークタスクを処理したり、いくつかのリクエストコールバックやその他のAPIをカプセル化したりします。ただし、ハンドラーは全能ではありません。ハンドラーのコードが標準化された方法で記述されている場合、メモリリークを引き起こす可能性があります。さらに、ハンドラー、メッセージ、メッセージがすべて互いに関連していることがわかっています。ハンドラーによって送信されたメッセージがまだ処理されていない場合、それを送信したメッセージとハンドラーオブジェクトは、スレッドメッセージの捨てによって保持されます。
ハンドラーはTLS(スレッドローカルストレージ)変数に属しているため、ライフサイクルとアクティビティは一貫性がありません。したがって、この実装方法は、ビューやアクティビティのライフサイクルと一致することを保証することが一般に困難であるため、正しいリリースを引き起こすのは簡単です。
例えば:
パブリッククラスのサンプアアクティビティはアクティビティを拡張します{プライベートファイナルハンドラーmleakyhandler = new Handler(){@overridepublic void handlemessage(message msg){// ...}}@overridedotected void oncreate(bundle savedinstancestate){super.oncreate(savedinstate for indinstanceate a and/ exectate a super.oncreate); minutes.mleakyHandler.postdelayed(new runnable(){@overridepublic void run(){/ * ... */}}、1000 * 60 * 10); //前のActive.finish();}}}に戻る10分間のメッセージを遅延したメッセージメッセージの遅延は、サンプアクリティビティで宣言され、MLeakyHandlerはメッセージキューメッセージのQuequeに押し込まれます。アクティビティが終了()によって削除されると、タスクの実行を遅らせるメッセージはメインスレッドに存在し続け、アクティビティのハンドラーリファレンスを保持しているため、アクティビティが仕上げ()に削減されます。
修正:アクティビティで非静的な内部クラスを使用しないでください。たとえば、ハンドラーを上記の静的と宣言すると、その生存期間はアクティビティのライフサイクルとは何の関係もありません。同時に、アクティビティは、アクティビティをコンテキストとして直接通過することを避けるために、弱い参照を通じて導入されます。次のコードを参照してください。
パブリッククラスのサンプアアクティビティはアクティビティを拡張します{/***静的内部クラスのインスタンスは、アウタークラスへの暗黙的*参照を保持しません。 msg){sampleactivityアクティビティ= mactivity.get(); if(activity!= null){// ...}}} myhandler mhandler = new myhandler(this);/***匿名のクラスのインスタンスは、「static」である場合、アウタークラスへの暗黙的な*の参照を保持しません。 run(){/ * ... */}};@overridedotected void oncreate(bundle savedinstancestate){super.oncreate(savedinstancestate); //メッセージを投稿し、10分間の実行を遅らせます。概要は、静的インナークラス +弱者を使用することをお勧めします。使用する前に空になるように注意してください。
WeakReferenceが先に言及されたので、ここではJavaオブジェクトのいくつかの参照タイプについて簡単に説明します。
Javaには、強力な参照、Softreference、WeakReference、およびPhatomreferenceの4つのカテゴリの参照があります。
Androidアプリケーションの開発では、メモリオーバーフローを防ぐために、大きなメモリを占有し、長い宣言サイクルを持ついくつかのオブジェクトを扱う場合、ソフト参照と弱い参照技術を可能な限り使用できます。
ソフト/弱い参照は、参照キュー(参照キュー)と組み合わせて使用できます。ソフト参照によって参照されるオブジェクトがガベージコレクターによってリサイクルされている場合、Java仮想マシンは、関連する参照キューにソフト参照を追加します。このキューを使用すると、ソフト/弱い参照のリサイクルリストを知ることができ、ソフト/弱い参照に失敗したバッファーをクリアできます。
アプリケーションが、多くの場所で使用されるデフォルトのアバター、デフォルトのゲームアイコンなど、多数のデフォルト画像を使用するとします。毎回写真を読むと、ファイルの読み取りにはハードウェア操作が必要であるため、パフォーマンスが低下するため、遅くなります。そのため、画像のキャッシュを検討し、必要に応じてメモリから直接読み取ります。ただし、画像が多くのメモリスペースを占有し、キャッシュするには多くの画像には多くのメモリが必要であるため、OutFmemoryの例外が発生する可能性が高くなる可能性があります。現時点では、この問題を回避するために、ソフト/弱い参照技術を使用することを検討できます。以下は、キャッシュのプロトタイプです。
最初にハッシュマップを定義し、ソフト参照オブジェクトを保存します。
プライベートマップ<文字列、softreference <bitmap >> imagecache = new hashmap <string、softreference <bitmap >>();
HashMapにビットマップのソフト参照を保存する方法を定義しましょう。
ソフト参照を使用した後、OutOfMemory例外が発生する前に、これらのキャッシュされた画像リソースのメモリ空間を解放することができ、それにより、メモリが上限に達し、クラッシュを回避するのを防ぎます。
OutOfMemory例外の発生を避けたい場合は、ソフト参照を使用できます。アプリケーションのパフォーマンスをもっと気にし、できるだけ早くより多くのメモリを占有するオブジェクトをリサイクルしたい場合は、弱い参照を使用できます。
さらに、オブジェクトが頻繁に使用されるかどうかを判断して、ソフト参照または弱い参照のために選択されるかどうかを判断できます。オブジェクトを頻繁に使用できる場合は、ソフト参照を使用してみてください。オブジェクトが使用されない場合は、弱い参照で使用できます。
OK、トピックに戻り続けてください。前述のように、静的ハンドラーの内側クラスを作成し、ハンドラーが保持しているオブジェクトに弱い参照を使用して、ハンドラーが保持するオブジェクトもリサイクル中にリサイクルできるようにします。ただし、これによりアクティビティの漏れは回避されますが、ルーパースレッドのメッセージキューにはまだ保留中のメッセージがある可能性があるため、アクティビティの破壊または停止中にメッセージキューメッセージキューのメッセージを削除する必要があります。
次の方法でメッセージを削除できます。
パブリックファイナルボイドremovecallbacks(runnable r);パブリックファイナルボイドremovecallbacks(実行可能r、オブジェクトトークン);パブリック最終void removecallbacksandmessages(オブジェクトトークン);パブリックファイナルボイドの取り外し(int what);パブリックファイナルボイドの取り外し(int what、object object);
静的なメンバー変数の使用を避けてください
メンバー変数が静的と宣言されている場合、そのライフサイクルはアプリプロセスライフサイクル全体と同じであることを知っています。
これにより、一連の問題が発生します。アプリプロセスがメモリレジデントになるように設計されている場合、アプリがバックグラウンドに削減されたとしても、メモリのこの部分はリリースされません。モバイルアプリの現在のメモリ管理メカニズムによると、大量のメモリを考慮したバックグラウンドプロセスが最初にリサイクルされます。このアプリがプロセスの相互保護を行った場合、アプリはバックグラウンドで頻繁に再起動します。電話が開発に参加したアプリをインストールすると、電話は一晩電源とトラフィックを消費し、アプリはユーザーがアンインストールまたはサイレントする必要があります。
ここでの修正は次のとおりです。
クラスの開始時に静的メンバーを初期化しないでください。怠zyな初期化を考慮することができます。
建築設計では、これを行う必要があるかどうかを考え、それを避けようとする必要があります。アーキテクチャをこのように設計する必要がある場合、このオブジェクトのライフサイクルを管理する責任があります。
オーバーライドを避けてくださいfinizeize()
1.ファイナライズメソッドは不確実な時間に実行され、希少なリソースをリリースするために依存することはできません。不確実な時間の理由は次のとおりです。
仮想マシンがGCを呼び出す時間が不確かです
ファイナライズデーモンスレッドがスケジュールされている時間は不確かです
2。ファイナライズメソッドは1回のみ実行されます。オブジェクトが復活したとしても、ファイナライズメソッドが実行された場合、GCが再び実行されたときに再び実行されません。その理由は次のとおりです。
ファイナライズメソッドを含むオブジェクトは、新しいときに仮想マシンによるファイナライズリファレンスを生成し、オブジェクトへの参照を生成します。ファイナライズメソッドが実行されると、オブジェクトに対応するファイナライズリファレンスがリリースされます。この時点でオブジェクトが復活した場合(つまり、強力な参照でオブジェクトを参照する)、そして2回目はGCです。ファイナライズリファレンスがそれに対応しなくなったため、ファイナライズメソッドは実行されません。
3。ファイナライズメソッドを含むオブジェクトは、GCをリリースする前に少なくとも2ラウンドのGCを通過する必要があります。
閉鎖されていないリソースによって引き起こされるメモリリーク
对于使用了BraodcastReceiver,ContentObserver,File,游标Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
一些不良代码造成的内存压力
有些代码并不造成内存泄露,但是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存。
例えば:
Bitmap 没调用recycle()方法,对于Bitmap 对象在不使用时,我们应该先调用recycle() 释放内存,然后才它设置为null. 因为加载Bitmap 对象的内存空间,一部分是java 的,一部分C 的(因为Bitmap 分配的底层是通过JNI 调用的)。 而这个recyle() 就是针对C 部分的内存释放。
构造Adapter 时,没有使用缓存的convertView ,每次都在创建新的converView。这里推荐使用ViewHolder。
要約します
对Activity 等组件的引用应该控制在Activity 的生命周期之内; 如果不能就考虑使用getApplicationContext 或者getApplication,以避免Activity 被外部长生命周期的对象引用而泄露。
尽量不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context ),即使要使用,也要考虑适时把外部成员变量置空;也可以在内部类中使用弱引用来引用外部类的变量。
对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:
将内部类改为静态内部类
静态内部类中使用弱引用来引用外部类的成员变量
Handler 的持有的引用对象最好使用弱引用,资源释放时也可以清空Handler 里面的消息。比如在Activity onStop 或者onDestroy 的时候,取消掉该Handler 对象的Message和Runnable.
在Java 的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋值为null,比如使用完Bitmap 后先调用recycle(),再赋为null,清空对图片等资源有直接引用或者间接引用的数组(使用array.clear() ; array = null)等,最好遵循谁创建谁释放的原则。
正确关闭资源,对于使用了BraodcastReceiver,ContentObserver,File,游标Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。
保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。
The above is a summary of the causes of memory leaks in Java introduced to you by the editor and how to avoid memory leaks (super detailed version).私はそれが誰にでも役立つことを願っています。ご質問がある場合は、メッセージを残してください。編集者は時間内に返信します。 wulin.comのウェブサイトへのご支援ありがとうございます!