あなたが求めているのはかなり難しい質問です。たった1つの質問だと思うかもしれませんが、実際には一度に複数の質問をしていることになります。私はそれをカバーしなければならないという知識で最善を尽くします、そしてうまくいけば、私が逃したかもしれないものをカバーするために他の何人かが参加します。
ネストされたクラス:はじめに
JavaでのOOPがどれほど快適かわからないので、これはいくつかの基本事項に当てはまります。ネストされたクラスとは、クラス定義が別のクラス内に含まれている場合です。基本的に2つのタイプがあります:静的なネストされたクラスと内部クラスです。これらの実際の違いは次のとおりです。
- 静的ネストクラス:
- 「トップレベル」と見なされます。
- 包含クラスのインスタンスを作成する必要はありません。
- 明示的な参照がないと、含まれているクラスメンバーを参照できません。
- 自分の寿命を持っています。
- 内部入れ子クラス:
- 常に包含クラスのインスタンスを構築する必要があります。
- 自動的に、含まれているインスタンスへの暗黙の参照があります。
- 参照なしでコンテナのクラスメンバーにアクセスできます。
- 寿命は、コンテナの寿命よりも長くないと想定されています。
ガベージコレクションと内部クラス
ガベージコレクションは自動ですが、使用されていると見なされるかどうかに基づいてオブジェクトを削除しようとします。ガベージコレクターはかなりスマートですが、完璧ではありません。オブジェクトへのアクティブな参照があるかどうかによってのみ、何かが使用されているかどうかを判断できます。
ここでの実際の問題は、内部クラスがそのコンテナーよりも長く存続している場合です。これは、包含クラスへの暗黙的な参照が原因です。これが発生する可能性がある唯一の方法は、包含クラスの外側のオブジェクトが、包含オブジェクトに関係なく、内部オブジェクトへの参照を保持している場合です。
これにより、内部オブジェクトは(参照を介して)存続しているが、それを含むオブジェクトへの参照は他のすべてのオブジェクトからすでに削除されているという状況につながる可能性があります。したがって、内部オブジェクトは常に包含オブジェクトを存続させます。参照してさせます。これに関する問題は、プログラムされていない限り、それが生きているかどうかを確認するために、包含オブジェクトに戻る方法がないことです。
この実現の最も重要な側面は、アクティビティ内にあるかドローアブルであるかに関係なく、違いがないことです。内部クラスを使用するときは常に系統立てて、それらがコンテナーのオブジェクトより長く存続しないようにする必要があります。幸いなことに、それがコードのコアオブジェクトでない場合、リークは比較的小さい可能性があります。残念ながら、これらのリークは見つけるのが最も難しいものの一部です。それらの多くは、リークが発生するまで気付かれない可能性があるためです。
ソリューション:内部クラス
- 含まれているオブジェクトから一時的な参照を取得します。
- 含まれているオブジェクトが、内部オブジェクトへの長期間有効な参照を保持する唯一のオブジェクトになるようにします。
- Factoryなどの確立されたパターンを使用します。
- 内部クラスが包含クラスのメンバーへのアクセスを必要としない場合は、それを静的クラスに変換することを検討してください。
- アクティビティ内かどうかに関係なく、注意して使用してください。
アクティビティとビュー:はじめに
アクティビティには、実行および表示できるようにするための多くの情報が含まれています。アクティビティは、ビューを持つ必要があるという特性によって定義されます。また、特定の自動ハンドラーもあります。指定するかどうかに関係なく、アクティビティには含まれるビューへの暗黙的な参照があります。
ビューを作成するには、ビューを作成できる場所と、ビューを表示できるように子があるかどうかを知る必要があります。これは、すべてのビューが(を介してgetContext()
)アクティビティへの参照を持つことを意味します。さらに、すべてのビューはその子(つまりgetChildAt()
)への参照を保持します。最後に、各ビューは、その表示を表すレンダリングされたビットマップへの参照を保持します。
アクティビティ(またはアクティビティコンテキスト)への参照がある場合は常に、レイアウト階層全体でチェーン全体をたどることができます。これが、アクティビティまたはビューに関するメモリリークが非常に大きな原因である理由です。大量のメモリが一度にリークされる可能性があります。
アクティビティ、ビュー、内部クラス
上記の内部クラスに関する情報を考慮すると、これらは最も一般的なメモリリークですが、最も一般的には回避されています。内部クラスがアクティビティクラスのメンバーに直接アクセスできるようにするのが望ましいですが、潜在的な問題を回避するために、多くの人はそれらを静的にしたいと思っています。アクティビティとビューの問題は、それよりもさらに深くなります。
漏洩したアクティビティ、ビュー、アクティビティコンテキスト
すべては、コンテキストとライフサイクルに起因します。アクティビティコンテキストを強制終了する特定のイベント(向きなど)があります。非常に多くのクラスとメソッドがコンテキストを必要とするため、開発者は、コンテキストへの参照を取得して保持することにより、コードを保存しようとする場合があります。たまたま、アクティビティを実行するために作成する必要があるオブジェクトの多くは、アクティビティが必要なことを実行できるようにするために、アクティビティライフサイクルの外部に存在する必要があります。オブジェクトが破棄されたときに、アクティビティ、そのコンテキスト、またはビューのいずれかへの参照が発生している場合、そのアクティビティとそのビューツリー全体がリークされています。
ソリューション:アクティビティとビュー
- ビューまたはアクティビティへの静的参照を作成することは、絶対に避けてください。
- アクティビティコンテキストへのすべての参照は、有効期間が短い必要があります(関数の期間)
- 長期間有効なコンテキストが必要な場合は、アプリケーションコンテキスト(
getBaseContext()
またはgetApplicationContext()
)を使用します。これらは暗黙的に参照を保持しません。
- または、構成の変更を上書きして、アクティビティの破棄を制限することもできます。ただし、これによって他の潜在的なイベントがアクティビティを破壊するのを防ぐことはできません。これを行うことはできますが、上記のプラクティスを参照することもできます。
Runnables:はじめに
ランナブルは実際にはそれほど悪くありません。私が意味する、彼らは可能性があることが、実際に我々はすでに危険区域のほとんどをヒットしました。Runnableは、作成されたスレッドに依存しないタスクを実行する非同期操作です。ほとんどの実行可能ファイルは、UIスレッドからインスタンス化されます。本質的に、Runnableを使用すると、もう少し管理された別のスレッドが作成されます。標準クラスのようにRunnableをクラス化し、上記のガイドラインに従っている場合、いくつかの問題が発生するはずです。多くの開発者がこれを行わないのが現実です。
使いやすさ、読みやすさ、論理的なプログラムフローから、多くの開発者は匿名インナークラスを使用して、上記で作成した例のように、ランナブルを定義します。これにより、上記で入力したような例が表示されます。匿名内部クラスは、基本的には個別の内部クラスです。まったく新しい定義を作成する必要はなく、単に適切なメソッドをオーバーライドするだけです。他のすべての点では、それは内部クラスです。つまり、コンテナへの暗黙的な参照を保持します。
ランナブルとアクティビティ/ビュー
わーい!このセクションは短い場合があります!Runnableは現在のスレッドの外部で実行されるため、非同期操作の実行時間が長くなるという危険があります。ランナブルがアクティビティまたはビューで匿名の内部クラスまたはネストされた内部クラスとして定義されている場合、いくつかの非常に深刻な危険があります。これは、前述のように、コンテナが誰であるかを知る必要があるためです。向きの変更(またはシステムの強制終了)を入力します。ここで、前のセクションに戻って、何が起こったのかを理解してください。はい、あなたの例は非常に危険です。
ソリューション:Runnables
- コードのロジックを壊さない場合は、Runnableを拡張してみてください。
- ネストされたクラスでなければならない場合は、拡張Runnableを静的にするために最善を尽くしてください。
- 匿名ランナブルを使用する必要がある場合は、使用中のアクティビティまたはビューへの長期間有効な参照を持つオブジェクトでそれらを作成しないでください。
- 多くのRunnableは、AsyncTasksと同じくらい簡単にできました。AsyncTaskを使用することを検討してください。これらはデフォルトでVM管理されるためです。
最後の質問
への回答ここで、この投稿の他のセクションでは直接取り上げられなかった質問に回答します。「内部クラスのオブジェクトが外部クラスよりも長く存続できるのはいつですか?」これを説明する前に、もう一度強調しておきます。アクティビティでこれを心配するのは正しいことですが、どこにでもリークが発生する可能性があります。簡単に説明するために、アクティビティを使用しない簡単な例を示します。
以下は、基本的なファクトリの一般的な例です(コードがありません)。
public class LeakFactory
{//Just so that we have some data to leak
int myID = 0;
// Necessary because our Leak class is an Inner class
public Leak createLeak()
{
return new Leak();
}
// Mass Manufactured Leak class
public class Leak
{//Again for a little data.
int size = 1;
}
}
これは一般的な例ではありませんが、十分に簡単に説明できます。ここでの鍵はコンストラクタです...
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Gotta have a Factory to make my holes
LeakFactory _holeDriller = new LeakFactory()
// Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//Store them in the class member
myHoles[i] = _holeDriller.createLeak();
}
// Yay! We're done!
// Buh-bye LeakFactory. I don't need you anymore...
}
}
現在、リークはありますが、ファクトリはありません。ファクトリーをリリースしても、すべてのリークはそれを参照しているため、メモリに残ります。外部クラスにデータがなくても問題ありません。これは、考えられるよりはるかに頻繁に発生します。クリエーターは必要ありません。クリエーションだけが必要です。そのため、一時的に作成しますが、無期限に使用します。
コンストラクタを少しだけ変更するとどうなるか想像してみてください。
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//WOW! I don't even have to create a Factory...
// This is SOOOO much prettier....
myHoles[i] = new LeakFactory().createLeak();
}
}
}
現在、これらの新しいLeakFactoriesのすべてがリークされています。あなたはそれをどう思いますか?これらは、内部クラスがどのタイプの外部クラスよりも長生きすることができるかについての2つの非常に一般的な例です。その外部クラスがアクティビティであった場合、それがどれほど悪化したかを想像してください。
結論
これらは、これらのオブジェクトを不適切に使用することの主な既知の危険性を示しています。一般的に、この投稿はあなたの質問のほとんどをカバーしているはずですが、私はそれが大げさな投稿であったことを理解しています。上記の慣例に従っている限り、漏洩の心配はほとんどありません。