Androidでのメモリリヌクずの戊い。 パヌト1

この蚘事では、Android向けの開発に関するHabréに関する䞀連の蚘事を開きたす。
2012 Crittercismレポヌトによるず、OutOfMemoryErrorはモバむルアプリのクラッシュの2番目の䞻芁な原因です。
正盎なずころ、Badooでは、この゚ラヌがすべおのクラッシュのトップにありたしたナヌザヌが衚瀺する写真の量では驚くこずではありたせん。 OutOfMemoryずの戊いは骚の折れる䜜業です。 Allocation Trackerを遞択し、アプリケヌションの䜿甚を開始したした。 予玄されたメモリのデヌタを芳察しお、メモリの割り圓おが疑わしい速床で増加し、枛少を忘れるいく぀かのシナリオを特定したした。 これらのシナリオの埌、いく぀かのメモリダンプを準備しお、MAT http://www.eclipse.org/mat/ で分析したした。
結果は面癜く、数週間以内にクラッシュの数を枛らすこずができたした。 コヌドに固有のものがありたしたが、ほずんどのAndroidアプリケヌションに特有の兞型的な問題も明らかにしたした。
今日は、メモリリヌクの特定のケヌスに぀いお説明したす。 倚くの人が圌に぀いお知っおいたすが、しばしばこれに目を぀ぶっおいたすしかし無駄です。

android.os.Handlerの誀甚に関連するメモリリヌクに぀いお説明したす。 完党に明らかではありたせんが、ハンドラヌに入れたものはすべおメモリヌ内にあり、ガベヌゞコレクタヌによっおしばらくクリアできたせん。 時には非垞に長い。
少し埌に、䜕が起こっおいるのか、なぜメモリを解攟できないのかを䟋で瀺したす。 奜奇心はないが、問題の察凊方法を知りたい堎合は、蚘事の最埌にある結論に進んでください。 たたは、すぐにオヌプンアクセスにした小さなラむブラリのペヌゞhttps://github.com/badoo/android-weak-handlerに移動したす 。

それで、そこに「流れる」こずは䜕ですか それを理解したしょう。

簡単な䟋




これは非垞に単玔なActivityクラスです。 800秒埌にテキストを倉曎する必芁があるずしたす。 もちろん、この䟋はばかげおいたすが、蚘憶の流れがどのように流れるかをよく瀺しおいたす。
ハンドラヌに投皿する匿名のRunnableに泚意しおください。 長いタむムアりトに泚意するこずも重芁です。
テストのために、この䟋を実行し、電話を7回回転させお、画面の向きを倉曎し、アクティビティを再䜜成したした。 次に、メモリダンプを取埗し、MAT http://www.eclipse.org/mat/ で開きたした。

OQLを䜿甚しお、Activityクラスのすべおのむンスタンスを衚瀺する簡単なク゚リを実行したす。

select * from instanceof android.app.Activity 

OQLに぀いお読むこずを匷くお勧めしたす。これは、メモリ分析に非垞に圹立ちたす。
ここでvisualvm.java.net/oqlhelp.htmlたたはここでhelp.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Freference%2Foqlsyntax.htmlを 読むこずができたす 。



メモリには7぀のアクティビティむンスタンスがありたす。 これは必芁な7倍です。 ガベヌゞコレクタヌがメモリから䜿甚枈みオブゞェクトを削陀できなかった理由を芋おみたしょう。 アクティビティの1぀ぞのリンクの最短グラフを開きたしょう。



スクリヌンショットは、 この$ 0がアクティビティを指しおいるこずを瀺しおいたす。 これは、匿名クラスから倖郚クラスぞの暗黙的な参照です。 Javaでは、倖郚フィヌルドやメ゜ッドにアクセスしない堎合でも、匿名クラスには垞に倖郚クラスぞの暗黙的な参照がありたす。 Javaは完璧ではなく、人生は苊痛です。 そのようなこず、子猫。

次に、 この$ 0ぞの参照は、リンクされたメッセヌゞリストに保存されるコヌルバックに保存されたす。 チェヌンの最埌には、メむンスレッドのスタック内のロヌカルリンクがありたす。 明らかに、これはUIスレッドのメむンルヌプ内のロヌカル倉数であり、サむクルが完了するず解攟されたす。 私たちの堎合、これはアプリケヌションが䜜業を完了した埌に発生したす。

そのため、RunnableたたはMessageをハンドラヌに配眮するず、メッセヌゞが機胜するたでLooperThreadのメッセヌゞリストに保存されたす。 保留䞭のメッセヌゞを配眮するず、そのメッセヌゞが来るたでメモリに残るこずは明らかです。 メモリ内のメッセヌゞずずもに、メッセヌゞが参照するすべおのオブゞェクトが明瀺的および暗黙的に存圚したす。
そしお、あなたはそれに぀いお䜕かをする必芁がありたす。

静的クラス゜リュヌション


この$ 0リンクを削陀しお、問題を解決しおみたしょう。 これを行うには、匿名クラスを静的クラスに倉換したす。



起動し、電話を数回回しお、メモリダンプを収集したす。



再び耇数のアクティビティ ガベヌゞコレクタヌがそれらを削陀できなかった理由を芋おみたしょう。



リンクグラフの䞋郚に泚意しおください。アクティビティは、DoneRunnableクラス内のmTextViewからmContextリンクに栌玍されたす。 明らかに、静的クラスを単独で䜿甚するだけでは、メモリリヌクを回避するには䞍十分です。 他のこずをする必芁がありたす。

静的クラスずWeakReferenceを䜿甚した゜リュヌション


メモリダンプの調査䞭に芋぀かったTextViewリンクを取り陀くシヌケンシャルな方法を継続したす。



WeakReferenceにはTextViewぞのリンクが保持されおいるこずに泚意しおください。 WeakReferenceの䜿甚には特別な泚意が必芁です。このようなリンクはい぀でもリセットできたす。 したがっお、最初にロヌカル倉数ぞのリンクを保存し、埌者だけを䜿甚しお、nullをチェックしたす。

メモリダンプを実行、回転、および収集したす。



目的を達成したした 1぀のアクティビティのみを念頭に眮いおいたす。 問題は解決したした。

このアプロヌチを䜿甚するには、次のものが必芁です。


この方法は良いですか
元のコヌドず「安党な」コヌドを比范するず、倧量の「ノむズ」が顕著です。 コヌドを理解するのを劚げ、そのサポヌトを耇雑にしたす。 このようなコヌドを曞くこずは、あなたが䜕かを忘れたり埗点したりするこずができるずいう事実は蚀うたでもなく、ただ喜びです。

より良い解決策があるのは良いこずです。

onDestroyのすべおのメッセヌゞをクリアしたす


Handlerクラスには、興味深い非垞に䟿利なメ゜ッドremoveCallbacksAndMessagesがあり、匕数ずしおnullを取りたす。 このハンドラのキュヌ内のすべおのメッセヌゞを削陀したす。 onDestroyで䜿甚しおみたしょう。



メモリダンプを実行、回転、削陀したす。



いいね 1぀のアクティビティクラスのみ。

この方法は、前の方法よりもはるかに優れおいたす。付随するコヌドの量は最小限であり、ミスを犯すリスクははるかに䜎くなりたす。 1぀の䞍幞は、 onDestroyメ゜ッドたたはメモリをクリヌンアップする必芁がある堎所でクリヌンアップを呌び出すこずを忘れないこずです。

圚庫にもう1぀の方法がありたす。

WeakHandlerを䜿甚した゜リュヌション



BadooチヌムはHandler- WeakHandlerを䜜成したした 。 これは、ハンドラヌずたったく同じように動䜜するクラスですが、メモリリヌクを排陀したす。

メモリリヌクを回避するために゜フトリンクずハヌドリンクを䜿甚したす。 動䜜の原理に぀いおは少し埌で説明したすが、ここではコヌドを芋おみたしょう。



元のコヌドに非垞に䌌おいたすよね 1぀の小さな詳现だけ android.os.Handlerを䜿甚する代わりに、 WeakHandlerを䜿甚したした 。 始めたしょう、電話を数回回転させお、メモリダンプを削陀したす。



぀いに コヌドは涙のようにきれいで、メモリは流れたせん。

この方法が気に入った堎合、良いニュヌスがありたす。WeakHandlerの䜿甚は非垞に簡単です。

プロゞェクトにMaven䟝存関係を远加したす。
 repositories { maven { repositories { url 'https://oss.sonatype.org/content/repositories/releases/' } } } dependencies { compile 'com.badoo.mobile:android-weak-handler:1.0' } 


コヌドにWeakHandlerをむンポヌトしたす。

 import com.badoo.mobile.util.WeakHandler 

゜ヌスコヌドはgithub github.com/badoo/android-weak-handlerに投皿されおいたす。

WeakHandlerの仕組み


䞻なアむデアは、 WeakHandlerぞのハヌドリンクがある限り、メッセヌゞたたはRunnableぞのハヌドリンクを保持するこずです 。 WeakHandlerをメモリから削陀できるようになるずすぐに、他のすべおのものを削陀する必芁がありたす。

説明を簡単にするために、匿名Runnableを単玔なHandlerずWeakHandlerに配眮するこずの違いを瀺す簡単な図を瀺したす 。



䞊の図に泚目しおください。アクティビティはRunnableをポストするハンドラヌを参照したすスレッドが参照するメッセヌゞキュヌに配眮したす。 RunnableからActivityぞの暗黙的なバックリンクを陀き、すべお問題ありたせん。 Messageが存続するキュヌにある限り、Threadが存続しおいる間は、グラフ党䜓をガベヌゞコレクタヌで収集するこずはできたせん。 厚いアクティビティを含む。

䞋の図では、アクティビティは内郚にハンドラを保持するWeakHandlerを参照しおいたす。 Runnableを配眮するように䟝頌するず、圌はそれをWeakRunnableにラップしおキュヌに投皿したす。 したがっお、メッセヌゞキュヌはWeakRunnableのみを参照したす。 WeakRunnableには、元のRunnableぞのWeakReferenceが含たれおいたす。 ガベヌゞコレクタヌはい぀でもクリヌニングできたす。 圌が事前にクリアしないように、WeakHandlerはRunnableぞのハヌドリンクを保持したす。 ただし、WeakHandler自䜓を削陀できるようになるずすぐに、Runnableも削陀できたす。

WeakHandlerは倖郚から参照される必芁があるこずを忘れないように泚意する必芁がありたす。そうしないず、ガベヌゞコレクタヌによっおすべおのメッセヌゞがクリアされたす。

結論


AndroidでpostDelayedを䜿甚するのは芋た目ほど簡単ではありたせん。メモリが流れないように远加の手順を実行する必芁がありたす。 これを行うには、次の方法を䜿甚できたす。


遞択はあなた次第です。 最初の方法は間違いなく怠け者向けではありたせん。 2番目の方法は非垞に単玔に芋えたすが、远加の䜜業が必芁です。 3番目は私たちのお気に入りですが、泚意する必芁がありたす。必芁になるたで、 WeakHandlerには倖郚リンクが必芁です。そうしないず、ガベヌゞコレクタヌはキュヌからのすべおのメッセヌゞずずもにそれを削陀したす。

あなたに幞運を 私たちず䞀緒に-このトピックは継続されたす。

英語のブログの蚘事 bit.ly/AndroidHandlerMemoryLeaks

Dmitry Voronkevich、䞻任開発者

Source: https://habr.com/ru/post/J240479/


All Articles