31

私は多くの検索を行ってきましたが、他の多くの人が同じ OOM メモリの問題を経験していることを知っていBitmapFactoryます。私のアプリは、使用可能な合計メモリが 4MB しか表示されませんRuntime.getRuntime ().totalMemory()。制限が 16MB の場合、ビットマップ用のスペースを確保するために総メモリが増加しないのはなぜですか? 代わりに、エラーがスローされます。

また、1.6MB の空きメモリがある場合、Runtime.getRuntime().freeMemory()「VM は 614400 バイトを割り当てることができません」というエラーが表示されるのはなぜですか? 私には十分な利用可能なメモリがあるようです。

この問題を除いて、私のアプリは完成しています。この問題は電話を再起動すると解消され、私のアプリだけが実行されます。デバイスのテスト (Android 1.5) に HTC Hero を使用しています。

この時点で、これを回避する唯一の方法は、何とか使用を避けることだと考えていBitmapFactoryます。

1.6MB の空きメモリがあるときに VM が 614KB を割り当てない理由について、これについてのアイデアや説明はありますか?

4

5 に答える 5

47

[(CommonsWare が以下で指摘しているように) この回答のアプローチ全体は、2.3.x (Gingerbread) までしか適用されないことに注意してください。Honeycomb Bitmap データの時点で、VM ヒープに割り当てられます。]

ビットマップ データは VM ヒープに割り当てられません。それへの参照は VM ヒープ (小さい) にありますが、実際のデータは基礎となる Skia グラフィックス ライブラリによってネイティブ ヒープに割り当てられます。

残念ながら、 BitmapFactory.decode...() の定義では、画像データをデコードできなかった場合は null を返すと書かれていますが、Skia の実装 (または、Java コードと Skia の間の JNI 接着剤) は、メッセージをログに記録します。 (「VM は xxxx バイトの割り当てを許可しません」) を見てから、「ビットマップ サイズが VM の予算を超えています」という誤解を招くメッセージと共に OutOfMemory 例外をスローします。

問題は VM ヒープではなく、ネイティブ ヒープにあります。ネイティブ ヒープは実行中のアプリケーション間で共有されるため、空き領域の量は、実行中の他のアプリケーションとそのビットマップの使用状況によって異なります。ただし、BitmapFactory が返されないことを考えると、呼び出しを行う前に、呼び出しが成功するかどうかを判断する方法が必要です。

ネイティブ ヒープのサイズを監視するルーチンがあります (Debug クラスの getNative メソッドを参照してください)。しかし、getNativeHeapFreeSize() と getNativeHeapSize() は信頼できないことがわかりました。したがって、多数のビットマップを動的に作成するアプリケーションの 1 つで、次のことを行います。

ネイティブ ヒープ サイズは、プラットフォームによって異なります。そのため、起動時に最大許容 VM ヒープ サイズをチェックして、最大許容ネイティブ ヒープ サイズを決定します。[マジック ナンバーは 2.1 と 2.2 でのテストによって決定されたものであり、他の API レベルでは異なる場合があります。]

long mMaxVmHeap     = Runtime.getRuntime().maxMemory()/1024;
long mMaxNativeHeap = 16*1024;
if (mMaxVmHeap == 16*1024)
     mMaxNativeHeap = 16*1024;
else if (mMaxVmHeap == 24*1024)
     mMaxNativeHeap = 24*1024;
else
    Log.w(TAG, "Unrecognized VM heap size = " + mMaxVmHeap);

次に、BitmapFactory を呼び出す必要があるたびに、呼び出しの前にフォームのチェックを行います。

long sizeReqd        = bitmapWidth * bitmapHeight * targetBpp  / 8;
long allocNativeHeap = Debug.getNativeHeapAllocatedSize();
if ((sizeReqd + allocNativeHeap + heapPad) >= mMaxNativeHeap)
{
    // Do not call BitmapFactory…
}

heapPad は、a) ネイティブ ヒープ サイズのレポートが「ソフト」であり、b) 他のアプリケーションのためにネイティブ ヒープにいくらかのスペースを残したいという事実を可能にするためのマジック ナンバーであることに注意してください。現在、3*1024*1024 (つまり 3M バイト) のパッドで実行しています。

于 2011-03-30T22:23:08.423 に答える
2

1.6 MB のメモリは多くのように見えますが、メモリがひどく断片化されているため、そのような大きなメモリ ブロックを一度に割り当てることができない場合があります (それでも、これは非常に奇妙に聞こえます)。

画像リソースの使用中に OOM が発生する一般的な原因の 1 つは、非常に高い解像度の JPG、PNG、GIF 画像を解凍する場合です。これらの形式はすべてかなりよく圧縮されており、スペースをほとんど占有しないことに注意する必要がありますが、画像を電話にロードすると、使用するメモリはwidth * height * 4 bytes. また、解凍が始まると、デコードステップのために他のいくつかの補助データ構造をロードする必要があります。

于 2009-12-24T13:45:25.780 に答える
2

Torid の回答に記載されている問題は、最新バージョンの Android で解決されているようです。

ただし、イメージ キャッシュ (専用のキャッシュ、または通常の HashMap のみ) を使用している場合は、メモリ リークが発生して、このエラーが発生しやすくなります。

私の経験では、ビットマップ参照をうっかり保持してメモリ リークを作成した場合、OP のエラー ( BitmapFactoryおよびネイティブ メソッドへの参照) がアプリをクラッシュさせるものです (ICS - 14 および +? まで)。

これを避けるには、ビットマップを「手放す」ようにします。これは、キャッシュの最終層で SoftReferences を使用することを意味し、ビットマップがそこからガベージ コレクションを取得できるようにします。これは機能するはずですが、それでもクラッシュが発生する場合は、 を使用してコレクション用に特定のビットマップを明示的にマークすることができbitmap.recycle()ますbitmap.isRecycled()

余談ですが、LinkedHashMapsは、かなり優れたキャッシュ構造を簡単に実装するための優れたツールです。特に、この例のようにハード参照とソフト参照を組み合わせた場合 (308 行目から) ... しかし、ハード参照を使用すると、自分自身をメモリに入れることができます。あなたが台無しにした場合の漏れの状況。

于 2012-04-01T19:44:13.837 に答える
0

これはかなり高レベルの答えですが、私にとっての問題は、すべてのビューでハードウェアアクセラレーションを使用していることでした。私のビューのほとんどにはカスタムビットマップ操作があり、これが大きなネイティブヒープサイズの原因であると考えましたが、実際、ハードウェアアクセラレーションを無効にすると、ネイティブヒープの使用量が4分の1に削減されました。

ハードウェアアクセラレーションは、ビューに対してあらゆる種類のキャッシュを実行し、独自のビットマップを作成するようです。すべてのビットマップはネイティブヒープを共有するため、割り当てサイズはかなり劇的に大きくなる可能性があります。

于 2011-12-01T22:28:54.087 に答える
0

通常、エラーは vm によってのみスローされるため、エラーをキャッチしても意味がありませんが、この特定のケースでは、エラーは jni グルー コードによってスローされるため、イメージをロードできなかったケースを処理するのは非常に簡単です。 OutOfMemoryError をキャッチします。

于 2011-07-18T20:51:22.433 に答える