web-dev-qa-db-ja.com

Android:OutofMemoryError:ビットマップのサイズがVM予算を超えています。理由はわかりません。

600x800ピクセルのJPEGを超えるギャラリーでOutOfMemory例外が発生しています。


環境

私は、約600x800ピクセルのJPG画像を含むギャラリーを使用しています。

私のコンテンツは単なる画像よりも少し複雑かもしれないので、各ビューをImageViewをJPGでラップするRelativeLayoutに設定しました。

ユーザーエクスペリエンスを「高速化」するために、私は4スロットの単純なキャッシュを持っており、(ルーパーで)表示された画像の左と右に約1画像をプリフェッチし、それらを4スロットのHashMapに保持します。

プラットフォーム

256 RAMおよび128ヒープサイズ、600x800画面のAVDを使用しています。これは、Entourage Edgeターゲットでも発生しますが、デバイスではデバッグが困難です。


問題

私は例外を受けています:

OutofMemoryError: bitmap size exceeds VM budget

そして、それは5番目の画像を取得するときに起こります。画像キャッシュのサイズを変更しようとしましたが、まだ同じです。


奇妙なこと:メモリの問題はないはずです

ヒープ制限が必要なものから非常に離れていることを確認するために、最初にダミーの8MB配列を定義し、参照しないでそのままにしてすぐにディスパッチされます。アクティビティスレッドのメンバーであり、次のように定義されています

static { @SuppressWarnings("unused")
byte dummy[] = new byte[ 8*1024*1024 ]; }    

その結果、ヒープサイズは約11MBになり、すべて無料です。 クラッシュし始めた後、私はそのトリックを追加しました。 OutOfMemoryの頻度を減らします。

現在、DDMSを使用しています。クラッシュの直前(クラッシュ後もほとんど変化しない)で、DDMSは次のように表示します。

ID  Heap Size   Allocated   Free       %Used    #Objects
1   11.195 MB   2.428 MB    8.767 MB   21.69%   47,156  

そして、詳細テーブルにはそれが示しています:

Type  Count  Total Size   Smallest   Largest   Median    Average
free  1,536  8.739MB      16B        7.750MB   24B       5.825KB

最大のブロックは7.7MBです。そして、LogCatは次のように述べています。

ERROR/dalvikvm-heap(1923): 925200-byte external allocation too large for this process.

中央値と平均値の関係を気にする場合は、使用可能なブロックのほとんどが非常に小さいと仮定するのがもっともです。ただし、ビットマップに十分な大きさのブロックがあり、7.7Mです。それでもまだ十分ではないのですか?

注:ヒープトレースを記録しました。割り当てられたデータの量を見ると、2Mを超えて割り当てられているようには感じられません。 DDMSによる空きメモリレポートと一致します。


  • ヒープの断片化のような問題が発生したのでしょうか?
  • 問題を解決/回避するにはどうすればよいですか?
  • ヒープはすべてのスレッドで共有されますか?
  • DDMSの読み出しを間違った方法で解釈していて、割り当てる900Kブロックが本当にないのでしょうか?もしそうなら、誰が私がそれを見ることができるか教えてもらえますか?

どうもありがとう

メイマン

34
Meymann

あなたの場合、特別なことは何もないと思います。メモリが足りないだけです。メモリ内にいくつかの600x800ビットマップを置くことはできません。それらはメモリを消費しすぎます。それらをSDに保存し、オンデマンドでメモリにロードする必要があります。それがまさにあなたのやっていることだと思います。

DDMSはJava=ヒープメモリの消費を表示します。ただし、DDMSに表示されないネイティブメモリもあります。また、私が理解している限り、ビットマップはネイティブメモリに作成されます。したがって、 DDMSは、これらのメモリの問題を追跡するのに適したツールではありません。メモリが解放されたことを確認し、不要になったイメージはガベージコレクターによって収集されます。

ガベージコレクターは独自のスケジュールで動作します。これが、不要になったビットマップでBitmap.recycle()メソッドを呼び出す必要がある理由です。このメソッドは、不足したネイティブメモリを解放します。この方法では、GCに依存せず、最大のメモリをできるだけ早く解放できます。

まず最初に、ビットマップをリークしないことを確認する必要があります。

これがニース post のメモリ割り当てです。より深く掘り下げるのに役立ちます

12
Fedor

それがあなたのためのオプションであるかどうかはわかりませんが、画像をスーパーサンプリングしてみましたか 画像をビットマップオブジェクトにロードしているときに、奇妙なメモリ不足の問題

5
Fedor

Froyoが新鮮だった2010年に質問されました。それ以来、多くのことが起こりました。 3.0以前は、ビットマップはJNIで割り当てられていました。記憶はDalvikの統計に表示されませんでした。それはもはや一体である必要はありません。 2.3より前のバージョンでは、logcatでJNIのメモリ統計を利用できませんでした(ビットマップデコード呼び出しJNI)。 4.4より多くのスペースを避難させた。 5.0アートのビッグバン。 2010年に戻ると、Nexus Oneは300MB未満のハイエンドでした。アプリの予算は約16MBでした。今日では、その記憶の約8倍があります。

0
Meymann

私も数週間前に同様の問題に直面し、画像を最適なポイントまで縮小することで解決しました。私はブログ here に完全なアプローチを記述し、OOM傾向コード対OOM証明コード here を含む完全なサンプルプロジェクトをアップロードしました。

聞いてからかなり時間が経ちました。

答えは2つの部分に分かれる必要があります。プレジンジャーブレッド:小さな写真を使用し、サブサンプリングを使用します。ビットマップを取得する前に、解放できない小さなアイテムを割り当てないようにしてください。生姜前、bmpのメモリは連続的である必要があり、VMメモリでカウントされませんでした。常にlogcatを見てください。Googleのメモリに関する講義をご覧くださいIO2011。Gingerをポストする方が簡単です。Honeycombなので、ビットマップもJava領域にカウントされます。jni領域はありません。不要なビットマップには常にリサイクルを使用してください。 GCを待つ必要はありません。

0
Meymann