34

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


環境

600x800 ピクセル前後の JPG 画像でギャラリーを使用しています。

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

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

プラットフォーム

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


問題

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

OutofMemoryError: bitmap size exceeds VM budget

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


奇妙なこと: メモリの問題があってはならない

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

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

その結果、ヒープ サイズはほぼ 11 MB になり、すべて空き状態になります。 クラッシュし始めた後にそのトリックを追加したことに注意してください。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 ブロックが実際にはありませんか? もしそうなら、誰か私がそれを見ることができる場所を教えてもらえますか?

どうもありがとう

メイマン

4

5 に答える 5

12

あなたの場合は特に何もないと思います。ただメモリが足りない。メモリに複数の 600x800 ビットマップを持つことはできません。メモリを消費しすぎます。SD に保存し、必要に応じてメモリにロードする必要があります。それはまさにあなたがしていることだと思います。

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

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

まず、ビットマップをリークしないようにする必要があります。

これは、メモリ割り当てに関する素敵な投稿です。より深く掘り下げるのに役立ちます

于 2010-07-06T09:00:55.207 に答える
5

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

于 2010-07-01T16:41:53.877 に答える
0

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

于 2014-06-12T10:47:39.967 に答える
0

私がそう尋ねてから、かなりの時間が経ちました。

答えは 2 つの部分に分割する必要があります: ジンジャーブレッド前: 小さな画像を使用し、サブサンプリングを使用し、おそらく 1 つの画面サイズの写真を使用し、良い結果が得られることを願っています。ビットマップを取得する前に、解放できない小さなアイテムを割り当てないようにしてください。Ginger 以前は、bmp のメモリは連続している必要があり、VM メモリにはカウントされませんでした。常にlogcatを見てください。Google IO 2011 のメモリに関する講義を参照してください。Post Ginger の方が簡単です。Honeycomb 以来、ビットマップは Java 領域でもカウントされます。jni 領域はありません。必要のないビットマップには常にリサイクルを使用してください。GCを待たないでください。

于 2014-06-13T11:14:37.913 に答える