19

私はAndroid 3.1、大きなヒープサイズオプション、約250Mの利用可能なメモリでテストしています。

アプリの設定で [テスト] ボタンを押すたびに次のコードが実行されるように設定しました。

float [][][]foo = new float[3][2048][2048];
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888);
bm.recycle();
bm  = null;
foo = null;

これには十分なメモリがあります。問題なくボタンを数回押すことができます。

しかし、ボタンを押し続けると、最終的に (20 ヒット未満) OutOfMemory で終了します。[通常は android.graphics.Bitmap.nativeCreate(Native Method)]

他に何も起こっていません。PreferencesActivity を離れる必要はありません。ボタンを押したときにも表示される小さなトーストがあるため、他の UI アクティビティがわずかに実行されています。

これは断片化によるものですか、それとも Android Bitmap コードや GC の恐ろしいバグですか? それとも私は愚かなことをしているだけですか?(バカなことさせてください…)

誰にも回避策がありますか?上記は、ユーザーが呼び出すたびにコードが実行する必要があることをかなり代表しているためです。現在、変数を細心の注意を払ってクリアしているにもかかわらず、数回使用すると終了します。(そして、これは長い間私を夢中にさせてきました!)

[アップデート]

断片化の問題または gc のバグであることを確認しました。ヒープ ダンプが示すように、処理中に約 26M でピークに達するアイドル時 (リークなし) に 5.6M しか使用していません。(また、ネイティブ ヒープは 4M 未満のままです。) 一方、Java ヒープは、テスト デバイスで 280M の制限まで拡張され、その時点で OutOfMemory 例外が発生し始めます。そのため、ピーク時に使用可能なヒープの 10% しか使用していませんが、OutOfMemory が発生しています。

[残念ながら、System.gc() への呼び出しを追加すると、上記の単純なテスト ケースが修正されます。(A) 違いはないはずであり、(B) 実際のコードで既に定期的に呼び出しているため、上記の単純なテスト ケースが単純すぎることを意味するため、残念です。]

他の誰かがこれに遭遇しましたか?回避策はありますか?アプリを再起動する適切な方法はありますか?

[アップデート]

次のバージョンでは、3 ~ 4 回の呼び出し (ボタンの押下) で確実に OutOfMemory が発生します。

float [][][]foo = new float[3][2048][2048];
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888);
int []bar = new int[3*2048*2048];
bm.recycle();
bm = null;
System.gc();
foo = null;
System.gc();
bar = null;
System.gc();

メモリ トレースは、ヒープが制限に達して終了するまで、各呼び出しで着実に成長していることを示しています。3 つの割り当てのいずれかを削除すると、均衡に達し、無期限に存続します。最後の gc() 以外をすべて削除すると、わずかに早く終了します。

これはフラグメンテーションの問題であり、gc のバグそのものではありません。誰かがそれを修正する方法を知っているなら、私に知らせてください。int[] 割り当てはビットマップを書き込むためのものであるため、2 次元配列として割り当てるオプションはありません (android ビットマップ ライブラリの制限)。

4

3 に答える 3

4

この問題の回避策があると思われる別の SO ページを次に示します。

画像を Bitmap オブジェクトにロードする際の奇妙なメモリ不足の問題

具体的には、エフライムによる回答(抜粋):

"1) BitmapFactory.decodeXYZ() を実行するたびに、必ず inPurgeable を true に設定して (できれば inInputShareable も true に設定して) BitmapFactory.Options を渡します。

「2) Bitmap.createBitmap(width, height, Config.ARGB_8888) は絶対に使用しないでください。つまり、絶対に使用しないでください!数回のパス後にメモリエラーが発生しないということは一度もありませんでした。recycle()、System.gc()、それは常に例外を発生させました.実際に機能するもう1つの方法は、ドローアブル(または上記の手順1を使用してデコードした別のビットマップ)にダミー画像を配置し、それを必要に応じて再スケーリングしてから、結果のビットマップを操作することです(楽しみのために Canvas に渡すなど) したがって、代わりに使用する必要があるのは: Bitmap.createScaledBitmap(srcBitmap, width, height, false). なんらかの理由でブルート フォース create メソッドを使用する必要がある場合は、少なくとも Config.ARGB_4444 をパスしてください。」

コメントでは、これで問題が解決したと言う人もいます。これは、ここの OP の問題と非常によく似ています。

Diane Hackborn が、Android 3.0 以降、ビットマップをネイティブ ヒープから割り当てるのではなく、通常のヒープから直接割り当てるとコメントしたことを付け加えておきます。これにより、ネイティブ ヒープの数値が無関係になる可能性があります。このページの hackbod のコメントを参照してください。

Android のビットマップ

これは、ビットマップの割り当てに関する Honeycomb の時点でのかなり大きな変更を意味していると思います。そのため、そのような割り当てにバグがある理由を説明できます (存在する場合)。この変更が recycle() コマンドにどのような影響を与えるかはわかりませんが、上記の Ephraim のコメントを考慮すると、答えは「あまり良いものではない」かもしれません。

ついに、

巨大なビットマップを取り込むために largeHeap を使用することは、特にデバイスの物理的限界に近づいている場合、他のアプリとうまく動作しないように見える可能性があります。どうすればそれを回避できるかわかりませんが、アプリが他のアプリを踏んだり、他のアプリがあなたのアプリを踏んだりするときに、多くの onPause() / onResume() アクティビティに備えてください。このSOの回答には、これに関する議論が含まれています:

Android でアプリケーションのヒープ サイズを検出する

于 2013-04-06T14:30:16.933 に答える
1

断片化を防ぐために、大きな配列とビットマップを一度割り当てて再利用することができます。

Android の場合、Android はアプリのリソースをある程度管理しようとするため、これにはいくつかの注意事項があります。たとえば、ActivityまたはViewが表示されていない場合はアンロードされ、後で再び表示された場合に再実行されます。そのため、大きなものはApplicationオブジェクトまたはstatic場所ごとに格納する必要があります。

これが設定ダイアログに使用されるだけの場合は、最初の使用時に予約し、その後は保持して、実行ごとにそれほど多くのメモリを使用しないようにする必要があります。めったに使用しない場合は、設定画面を離れた後にアプリケーションを再起動する必要があります。適切に作成されていれば、ユーザーはそれに気付く必要がなく、新鮮でメモリに優しいプロセスが再び得られます。

于 2013-02-16T14:51:54.083 に答える