Android開始中のOOM異常

5080 ワード

Androidに基づいてアプリケーションを開発する場合、Out Of Memoryが頻繁に現れる可能性があります 異常.この問題に困っているときはまず原因を理解しなければなりません.ポイントはもちろん、どのように処理するかを知る必要があります.
1、OOMの具体的な原因.①1つのプロセスのメモリは、javaの2つの部門で構成できます. メモリ、Cの使用 メモリを使用すると、この2つのメモリの合計は16 M未満でなければなりません.そうしないと、おなじみのOOMが表示されます.
②メモリがJavaに割り当てられると、その後このメモリが解放されてもJavaの使用しかできなくなります.これはjava仮想マシンでメモリをいくつかに分けてキャッシュする原因と関係があると思います.どうせCはこのメモリを使用したくないので、Javaが突然大きなメモリを占有してしまったら、すぐに解放されてもCが使用できるメモリは = 16 M-Javaが瞬時に占める最大メモリ. 
③Bitmapの生成はmallocによるメモリ割り当てであり,Cのメモリを占有する.
2、何かアドバイスがありますか.
①Javaで使用されなくなったリソースについては、nullに設定して、ゴミ回収器があなたのために働くことを期待しないでください.nullに設定しないと、リソース回収に一定の影響が与えられます.
②staticメソッドやstaticメンバーはなるべく少なくしてください.staticのメソッドやメンバーが外部で使用されているため、外部の牽引オブジェクトが解放されていないとstaticのクラス全体が解放されず、メモリが漏れることになります.
③使用しなくなったbitmapについてはrecycleメソッドを手動で呼び出しnullに設定する.画像はできるだけソフトリファレンス方式を使うことで、ゴミの回収を速めることができます.
 
3、不適切な処理.
ネット上では他の処理方法もありますが、総じて個人的にはあまり適切ではないと思います.次のようになります.
①圧縮ピクチャに対して、ピクチャにoptionを設定します.個人的には望ましくないと思います.画像が本来はっきりしていない場合が多く、圧縮された後の品質に大きな問題があり、個人の取捨選択を見なければならない.
②appのheapsizeサイズを変更します.個人的にはこれも望ましくないと思います.これは本質的に問題を解決するものではありません.OOMはあなたのどこかでメモリが漏洩した可能性があります.これはOOMの発生を遅らせただけで、ある程度OOMの発生率を下げた可能性があります(heapsizeの最大値に達していないうちにGCが発生した可能性があります).Androidシステムにはデフォルトのheapsizeの値がありますが、システム全体や他のアプリケーションにどのような影響を及ぼすかは分かりませんので、デフォルトを使用します.
 
4、多くのOOMはBitmapが大きすぎるからです.ここではBitmapを解決するOOMに特化しています.最も核を発しているのは可視範囲のBitmapだけをロードすることです
このような状況を考えてみると、GridViewやListViewでは、データ量が5000で、画面ごとに20要素しか表示されていないので、見えません.Bitmapを含めて保存する必要はありません.だから私たちはそんなに見えるBitmapだけをメモリに残して、それらは見えないので、解放します.要素が滑り出したらBitmapをロードします
4.1 Bitmapのメモリをアクティブに解放
本質的な考え方. 
1、可視領域のBitmapのみをロードする
  2、スライド時にロードしない
  3、スライドを停止した後、可視領域の画像の再ロードを開始する
  4、可視領域から滑り出したBitmapの内在を解放する
実装プロセスが複雑
1、GridView/ListViewのスライドイベントをリスニングする必要があります.これは簡単です.AbsListView#setOnScrollListener(OnScrollListener l)
     2、Bitmap#recycle()メソッドをアクティブに呼び出すと、このBitmapがView(ImageViewなど)に参照されているかどうかを判断する必要があります.参照されている場合、recycle()メソッドを簡単に呼び出すことはできません.これにより、Viewが回収されたBitmapを使用しているという異常が発生します.
    3、GridView/ListViewのスライド状態が絶えず変化する可能性があるため、つまりスライド->停止->スライドを制御するために独自のスレッドを設計しなければなりません.このような状態は絶えず変化する可能性があります.これにより、私たちのスレッドのrun()メソッドの論理が複雑になり、複雑になると、問題がより多くなる可能性があります.
    以上の点から、この方式は最良ではないので、お勧めしません.
2つ目はcacheを設定することです   
 この方法では、まずcacheを利用しています.cacheは重要なものだと思います.Bitmapのメモリを単独で1つの場所に置いて管理しています.この場所はcacheです.その容量は一定です.私たちはこのcacheに要素を追加したり、要素を削除したりすることができます.
Lrucache
1、これは実はLinkedHashMapで、いつでも、一つの値がアクセスされると、キューの開始位置に移動されるので、LinkedHashMapを使う理由でもあります.頻繁に移動操作をするので、パフォーマンスを向上させるためにLinkedHashMapを使います.cacheがいっぱいになったとき、このときにもう一つの値をcacheに追加すると、キューに最後の値(lruアルゴリズム)はキューから削除され、この値はGCで回収される可能性があります.
    2、メモリをアクティブに解放したいなら、entryRemoved(Boolean,K,V,V)メソッドを書き換えることができます.
    3、このクラスはスレッドが安全で、マルチスレッドの下でこのクラスを使うと、問題はありません.
synchronized (cache) {
     if (cache.get(key) == null) {
         cache.put(key, value);
   }}

LruCacheのAPILevelは12、つまりSDK 2.3.x以下では使えませんが、大丈夫です.LruCacheのソースコードは複雑ではありません.自分のプロジェクトディレクトリに直接コピーすればいいです.
AsyncTask
このクラスも重要でよく使われるクラスです.ThreadとHandlerをカプセル化しているので、Handlerに注目する必要はありません.バックグラウンドスレッドではUIを更新できないことを知っていますが、バックグラウンドスレッドで何かをした後、UIを更新するのが一般的です.一般的にはUIスレッドに関連付けられたHandlerにメッセージを送信し、Handlerでは、このメッセージを処理してUIを更新します.AsyncTaskを使用すると、Handlerに注目する必要はありません.このクラスにはいくつかの重要な方法があります.
    1、onPreExecute():UIスレッドで呼び出され、このtaskが実行されるとすぐに呼び出されます.この方法では、通常、ユーザーに通知するための待機ダイアログを表示するなどのタスクを作成するために使用されます.
    2、doInBackground(Params...):この方法は名前から分かるように、バックグラウンドスレッドで実行されています.この方法では、ダウンロードアクセスネットワーク、操作ファイルなど、時間のかかることをします.この方法では、publishProgress(Progress...)を呼び出すことができます.現在のタスクの進行状況を呼び出します.このメソッドを呼び出すと、UIスレッドで実行されるonProgressUpdate(Progress...)メソッドが呼び出されます.
    3、onProgressUpdate(Progress...):publishProgress()メソッドを呼び出した後、UIスレッドで実行します.このメソッドは、待機ダイアログを表示したり、テキスト形式のlogを表示したり、toastダイアログを表示したりするなど、UIに任意の形式の進捗を表示するために使用されます.
    4、onPostExecute(Result):taskが終了すると呼び出され、UIスレッドで実行されます.
    5、taskをキャンセルすると、いつでもcancel(Boolean)を呼び出してタスクをキャンセルすることができます.cancel()メソッドを呼び出すと、onCancelled(Object)メソッドが呼び出され、onPostExecute(Object)メソッドは呼び出されません.doInBackground(Object[])メソッドでは、isCancelled()メソッドでタスクがキャンセルされたかどうかを確認できます.
    6、何時ルール
AsyncTaskインスタンスはUIスレッドで作成する必要があります   
Excute(Params...)メソッドはUIスレッドで呼び出さなければなりません.onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()メソッドを手動で呼び出す必要はありません.1つのタスクは1回しか実行できません. 

  •  
    全体的な考え方. 
      1、常にcacheからBitmapを取りに行き、Bitmapを取ったらそのままこのBitmapをImageViewの上に設定します.
        2、キャッシュに存在しない場合は、taskを起動してロードします(ファイルからでも、ネットワークからでも).
        3、各ImageViewにはtaskがバインドされている可能性があります.そのため、このImageViewはそれに関連するtaskを得る方法を提供しなければなりません.なぜそうするのですか.ImageViewにtaskをバインドする前に、元のtaskをキャンセルしなければならないからです.
        4、考え方は简単ですが、细かい点が多く、文字で一つ一つ说明するのも现実的ではありません.私はdemoをアップロードして、みんなはコードと文章を参考にして理解することができます.
    まとめ:総じてコード実装の過程で、各段階はOOMの元凶になる可能性があり、画像復号の過程で現れただけである.だから符号化の過程でいくつかの比較的良い符号化方式を採用すべきで、時にはプログラムを何度も最適化してメモリのオーバーヘッドを減らす必要がある.Javaのゴミ回収メカニズムはある程度私たちに便利さを持って、しかし埋め込み式の方面でまだいくつか不足して、結局埋め込み式の資源はとても貴重で、いったん制御がよくないとOOMが現れます.コードの最適化をする前に私達もごみの回収のメカニズムを深く理解しなければならなくて、このようにやっと効果的にメモリの消耗を制御することができます