10

オプションを有効にする前largeHeapは、大きなビットマップを処理していましたが、アプリケーションで使用可能なメモリのほぼ全体を消費し、ナビゲーションでリサイクルして新しいものをロードすると、使用可能なほぼすべてのヒープで機能します。ただし、一部の操作でもう少しメモリが必要になると、アプリケーションがクラッシュします。そこでlargeHeap=true、もう少しメモリを増やすことができました。

しかし、これを行うと予期しない動作がrecycle()発生します。ビットマップのメソッドはほとんどの場合機能しないようです。58Mbのメモリで機能した(場合によってはをスローすることを超えるOutOfMemoryException)アプリケーションは、メモリを指数関数的に消費し、成長を続けます(今のところテスト私は231Mbの割り当てられたメモリに到達しました)、予想される動作は、メモリ管理が機能し続け、アプリケーションが60Mbを超えて使用しないことです。

どうすればそれを回避できますか?または、ビットマップを効率的にリサイクルしますか?

OutOfMemoryError編集:実際、私はデバイスに390Mbを超えるメモリを割り当てるときにそれを与えるようにしました。GC_ *ログを読むと、3.8Mbを解放したGC_FOR_ALLOCだけが時々解放されましたが、他のGC実行ではほとんど解放されなかったことがわかりました。

4

5 に答える 5

21

大規模なビットマップを効率的に処理する方法がいくつか含まれているDisplaying Bitmaps Efficientlyをご覧ください。

  • 大きなビットマップを効率的にロードする
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;

これにより、ダウンロードする前に画像のサイズが得られます。これに基づいて、デバイスのサイズを確認し、ドキュメントの説明を使用calculateInSampleSize()してスケーリングできます。decodeSampledBitmapFromResource()

画像をどれだけスケーリングする必要があるかを計算すると、

if (imageHeight > reqHeight || imageWidth > reqWidth) {
      if (imageWidth > imageHeight ) {
          inSampleSize = Math.round((float)imageHeight / (float)reqHeight);
      } else {
          inSampleSize = Math.round((float)imageWidth / (float)reqWidth);
      }
    }
int inSampleSize = Math.min(imageWidth / reqWidth,imageHeight / reqHeight);

設定できるのはinSampleSize

 options.inSampleSize = inSampleSize;

最後に、必ず電話してください。

options.inJustDecodeBounds = false;

それ以外の場合は、ビットマップを次のように返しますnull

  • UI スレッド外でのビットマップの処理

    UI スレッドでビットマップを処理することは決して安全ではないため、常にバックグラウンド スレッドで処理し、処理が完了した後に UI を更新することをお勧めします。

  • ビットマップのキャッシュ

    LruCacheは API 12 から利用できますが、以下のバージョンを使用して興味がある場合は、サポート ライブラリでも利用できます。したがって、画像のキャッシュはそれを使用して効率的に行う必要があります。また、イメージにDiskLruCacheを使用して、外部ストレージに長期間保持することもできます。

  • キャッシュのクリア

    画像のサイズが大きすぎると、画像をキャッシュしても原因となるOutOfMemoryErrorことがあります。その場合、画像が範囲外または長期間使用されていないときにキャッシュをクリアして、他の画像をキャッシュできるようにすることをお勧めします。

    同じデモの例を作成しました。ここからダウンロードできます

于 2012-10-10T12:13:19.323 に答える
3

ケースは期待どおりに動作します。Honeycomb の前recycle()は、無条件にメモリを解放していました。しかし、3.0 以降では、ビットマップは通常のガベージ コレクション メモリの一部です。デバイスに十分な RAM があり、JVM が 58M の制限を超える割り当てを許可したため、ガベージ コレクターが満足し、ビットマップによって占有されているメモリを再利用するインセンティブがなくなりました。

制御された量のRAMを備えたエミュレーターで実行するか、デバイスにメモリを消費するサービスをロードすることでこれを確認できます-GCは動作にジャンプします。DDMSを使用して、メモリ使用量をさらに調査できます。

ビットマップ メモリ管理のいくつかのソリューションを試すことができます: Androidの ビットマップ ビットマップ メモリ リーク http://blog.javia.org/how-to-work-around-androids-24-mb-memory-limit/、しかし公式から始めます@Lalit Poptani詳細な回答で説明されているAndroid ビットマップのヒント

ビットマップをテクスチャとして OpenGL メモリに移動すると、パフォーマンスに影響することに注意してください (ただし、これらのビットマップを最終的に OpenGL でレンダリングする場合は完璧です)。テクスチャと malloc ソリューションの両方で、もう使用しないビットマップ メモリを明示的に解放する必要があります。

于 2012-10-14T06:25:59.940 に答える
2

Bitmaps間違いなく@Lalit Poptaniの答えはそれを行う方法です。それらが非常に大きい場合は、実際にスケーリングする必要があります。server-side時間も短縮できるので、可能であれば実行することをお勧めしますNetworkOperation

a の実装に関してはMemoryCacheDiskCacheこれが最善の方法ですが、既存のライブラリを使用することをお勧めしますIgnition. 、Heap後で空にならないので、GCおそらくあなたもいくつか持っていると推測できるからですmemory leaks

于 2012-10-16T14:41:03.837 に答える
1

Android でのビットマップの取り扱いには十分注意する必要があります。言い換えると、4 GB の RAM を搭載したシステムでも、ビットマップの処理に注意する必要があります。これらの人はどれくらいの大きさで、たくさんありますか?大きい場合は、切り刻んで並べる必要がある場合があります。システム RAM とは異なる動物であるビデオ RAM を使用することを忘れないでください。

ハニカム以前は、ビットマップは C++ レイヤーに割り当てられていたため、RAM の使用状況は Java からは見えず、ガベージ コレクターからもアクセスできませんでした。RGB24 色空間の 3 MP 非圧縮ビットマップは、約 9 ~ 10 メガバイト (約 2048x1512) を使用します。そのため、大きな画像は簡単にヒープをいっぱいにしてしまいます。また、ビデオ RAM (専用 RAM の場合もあれば、システムと共有される場合もあります) に使用されているものは何でも、通常、データは圧縮されずに保存されることを覚えておいてください。

基本的に、ハニカム以前のデバイスをターゲットにしている場合、Bitmap オブジェクトを C++ プログラムをコーディングするのと同じように管理する必要があります。通常、ビットマップ recycle() onDestory() を実行すると、画像が多くない場合に機能しますが、画面に大量の画像がある場合は、オンザフライで処理する必要がある場合があります。また、別のアクティビティを起動する場合は、onPause() と onResume() にロジックを入れることを検討する必要がある場合があります。

ビデオ RAM にない場合は、Android ファイル システムまたは SQLite を使用して画像をキャッシュすることもできます。繰り返しデータが多い .jpg や .png などの形式を使用している場合は、RAM にキャッシュすることで回避できる場合があります。

于 2012-10-16T12:37:43.297 に答える
1

あなたのジレンマに対処するために、これは予想される動作だと思います。

メモリを解放したい場合は、ときどき を呼び出すことができますがSystem.gc()、実際にはほとんどの場合、ガベージ コレクション自体を管理させる必要があります。

私がお勧めするのは、各ビットマップが占有しているバイト数を計算することにより、独自のメモリ使用量を追跡する何らかの種類の単純なキャッシュ (URL/ファイル名からビットマップへ) を保持することです。

/**
 * Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config
 * @param width
 * @param height
 * @param config
 * @return
 */
public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){
    long pixels=width*height;
    switch(config){
    case ALPHA_8: // 1 byte per pixel
        return pixels;
    case ARGB_4444: // 2 bytes per pixel, but depreciated
        return pixels*2;
    case ARGB_8888: // 4 bytes per pixel
        return pixels*4;
    case RGB_565: // 2 bytes per pixel
        return pixels*2;
    default:
        return pixels;
    }
}

次に、アプリが使用しているメモリの量と利用可能な量をクエリします。おそらくその半分を取り、画像キャッシュの合計サイズをそれ以下に維持しようとします。これは、リストから古い画像を単純に削除 (逆参照) することによって行われます。この制限に対して、リサイクルではありません。ビットマップがキャッシュから参照解除され、どのビューでも使用されていない場合は、ガベージ コレクターにビットマップをクリーンアップさせます。

/**
 * Calculates and adjusts the cache size based on amount of memory available and average file size
 * @return
 */
synchronized private int calculateCacheSize(){
    if(this.cachedBitmaps.size()>0){
        long maxMemory = this.getMaxMemory(); // Total max VM memory minus runtime memory 
        long maxAllocation = (long) (ImageCache.MEMORY_FRACTION*maxMemory);
        long avgSize = this.bitmapCacheAllocated / this.cachedBitmaps.size();
        this.bitmapCacheSize = (int) (maxAllocation/avgSize);
    }
    return this.bitmapCacheSize;
}

を使用しないことをお勧めします。recycle()断続的な例外が多数発生し (ファイナライズされたように見えるビューが再利用されたビットマップにアクセスしようとした場合など)、一般的にバグがあるようです。

于 2012-10-15T02:41:53.567 に答える