0

起動時にかなり大量の JSON を解析するアプリを開発しました。これにより、ヒープが限界近くまで大きくなります。少し後に、アプリはいくつかのビットマップをロードしています。ビットマップの割り当てと Java オブジェクトの割り当てに同じメモリ領域を使用しているように見える Honeycomb 以降のデバイスでは、JSON 解析に必要なメモリがそれまでに解放されているため、問題は発生していません。ただし、Honeycomb 以前のデバイスでは OutOfMemoryErrors が発生します

ヒープ サイズが決して下がらないため、dalvik は外部割り当て (ビットマップなど) 用に予約されたメモリを増やすことができないようです。

これは、クラッシュのログ出力の一例です

GC_EXTERNAL_ALLOC freed 601K, 81% free 5504K/27591K, external 4809K/5561K, paused 58ms
586224-byte external allocation too large for this process.

ご覧のとおり、ヒープには十分なメモリがありますが、外部割り当てにはあまり使用できません。

dalvik にデフラグとヒープの縮小を強制する方法はありますか? または、外部割り当て用に予約されたメモリではなく、ヒープ上でビットマップ割り当てを強制することはできますか? または、私が見逃しているこの問題に対する他のアプローチはありますか?

** アップデート **

以下は、より多くのログ出力です (特定のデバイスは dalvik-heap メッセージをログに記録しません)。

起動時に JSON が解析されると、dalvik ヒープが大きくなります。

GC_CONCURRENT freed 800K, 19% free 12717K/15687K, external 2637K/2773K, paused 2ms+5ms
GC_CONCURRENT freed 871K, 19% free 13857K/16903K, external 2637K/2773K, paused 2ms+5ms
GC_CONCURRENT freed 1106K, 19% free 14766K/18055K, external 2637K/2773K, paused 3ms+5ms
GC_CONCURRENT freed 818K, 16% free 15946K/18951K, external 2637K/2773K, paused 3ms+6ms
GC_CONCURRENT freed 825K, 15% free 17151K/20167K, external 2637K/2773K, paused 2ms+6ms
GC_CONCURRENT freed 830K, 15% free 18356K/21383K, external 2637K/2773K, paused 2ms+5ms
GC_CONCURRENT freed 814K, 14% free 19519K/22535K, external 2637K/2773K, paused 2ms+6ms
GC_CONCURRENT freed 823K, 13% free 20720K/23751K, external 2637K/2773K, paused 2ms+5ms
GC_CONCURRENT freed 814K, 13% free 21873K/24903K, external 2637K/2773K, paused 3ms+6ms
GC_CONCURRENT freed 813K, 12% free 23016K/26055K, external 2637K/2773K, paused 2ms+5ms
GC_CONCURRENT freed 1771K, 15% free 23205K/27207K, external 2637K/2773K, paused 2ms+5ms

それが完了すると、ほとんどのヒープが再び正常に解放されます。

GC_EXPLICIT freed 19207K, 83% free 4735K/27207K, external 2736K/2773K, paused 140ms

この時点で、20 MB の空き Dalvik ヒープ スペースがあり、少し遅れてビットマップの割り当てを開始します。

GC_EXTERNAL_ALLOC freed 254K, 83% free 4814K/27207K, external 2771K/2773K, paused 47ms
GC_EXTERNAL_ALLOC freed 721K, 83% free 4880K/27207K, external 3881K/4131K, paused 50ms
GC_EXTERNAL_ALLOC freed 235K, 83% free 4870K/27207K, external 5398K/5561K, paused 62ms

デバイスの合計制限は 32 MB のようですが、dalvik ヒープはそれらのうち 27 MB を使用し、ダウンすることはないため、ビットマップを割り当てるための外部メモリ領域が不足しています。

4

2 に答える 2

0

まず、エミュレーターで実行していますか?ヒープサイズが少し小さすぎるように見えるからです。私の計算によると、合計約 11MB の合計使用量 (dalvik + ネイティブ) の後にクラッシュしていますが、それはごくわずかです。確認のために、クラッシュ後にこのような行がありますか?

08-22 18:16:24.209: I/dalvikvm-heap(471): Clamp target GC heap from 24.610MB to 24.000MB

最大ヒープ サイズ (dalvik + ネイティブ) は、デバイスによって異なります。最小値は 16MB で、画面解像度が大きいほど大きくなります。480x800 のデバイスは、通常 32MB を超えています。エミュレーターの AVD 設定でヒープ サイズを制御できます。実際のデバイスを反映するのに十分な大きさであることを確認してください。

あなたの質問に関しては、ヒープを自分で支払うべきではありません。VM はそれを自動的に行います。ネイティブ ヒープ全体の混乱により、GC が問題を推定するのが難しくなるのは事実です。そのため、大規模な割り当ての前に手動で GC を実行することでおそらく回避できる OutOfMemoryErrors がいくつか発生します。

より具体的には、ビットマップが解析されるとき(のような呼び出し中BitmapFactory.decodeXXX())に大きな割り当てが発生します。私の経験から、彼らの前に走ることは実際に役立ちSystem.gc()ます。これは、ハニカム以前のデバイスでのみ行う必要があります。

もう 1 つのヒントは、ネイティブ割り当てをできるだけ早く解放することです。ビットマップのピクセルはネイティブ ヒープにあります。それらはビットマップ ファイナライザーでのみ解放されます。ご存じのとおり、実行には時間がかかります。このメモリは、システム GC を数回実行した後にのみ解放されます。これが、手動で解放することをお勧めする理由です。ビットマップの処理が完了したら、すぐに呼び出しBitmap.recycle()てネイティブ メモリを解放します。

于 2013-07-29T21:34:24.920 に答える
0

これまでとは全く違う視点で挑戦してみたいと思います。Android でのメモリの問題 = ネイティブ ヒープとビットマップが 90% の確率で..しかし、これはここでは当てはまりません。私が思い出す限り、ネイティブ ヒープには制限がほとんどなく、必要なだけ大きくすることができます。私が覚えている唯一の制限は、使用されているdalvik ヒープと使用されているネイティブ ヒープがアプリのヒープ制限を超えていないことを確認する dalvik GC での手動テストです。

問題がネイティブ ヒープとは関係なく、dalvik ヒープだけである場合はどうでしょうか。

失敗した 500kb の割り当てがネイティブ ヒープ (ビットマップ ピクセル用) にあると想定しました。この割り当てが実際に dalvik ヒープにある可能性があります。ヒープが非常に断片化されているため、システムは dalvik ヒープで連続した 500kb のチャンクを見つけることができず、クラッシュします。

どのビットマップデコード関数を使用していますか? 一部のメソッドはリソースからネイティブ ヒープに直接デコードし、一部のメソッド (バイト配列を使用するメソッドなど) は dalvik ヒープのバッファーからデコードしているため、大きな割り当てが必要になります。

これを修正するには、ビットマップ デコード関数を変更して、dalvik ヒープにまったく割り当てないようにするか、ソースで断片化の問題全体を修正しましょう。

一般的な断片化の問題を回避するためのいくつかのアイデアを次に示します (最初の JSON 処理部分でこれらを適用します)。

  1. System.gc()戦略的に配置された場所で手動で実行します。人気のある場所は、アクティビティ onDestroy のような大きなオブジェクトの破壊中です。または、大きな JSON チャンクで終了するたびに、そこに手動 GC を投入します。

  2. システム GC の空きリソースをより速く支援します。これにより、そもそもヒープが断片化するのを防ぐことができます。その方法については、ネット上にたくさんの資料があります。私の頭の上からいくつか:

    • 小さな割り当てが頻繁に発生する場合は、オブジェクトのプールを作成し、新しいオブジェクトを割り当てる代わりにプール内のオブジェクトを再利用するなど、メモリの再利用スキームを採用してみてください。この最適化は、Android アダプターでよく見られます (リストビューでのビューの再利用など)。
    • 参照グラフから変数を明示的に切断し、簡単な GC 用にマークするために、変数が必要ない場合は変数を手動で null に設定します (データ コレクションを明示的にクリアする場合も同様です)。
    • 文字列の連結など、多くの操作がある場合は不変クラスを避けStringBuilder、通常Stringの s の代わりに使用します
    • ファイナライザーを完全に避ける
    • 循環参照を避けるか、少なくともそれらの 1 つを弱参照に変更します
    • 一般的に必要な場所で弱参照を使用する

ヒープのごく一部しか使用していないときにヒープが巨大なサイズに成長しないようにすることで、成功の度合いを測定できます。

于 2013-07-30T06:50:34.183 に答える