質問: 0; i < viewGroup.getChildCount(); i++) { cleanupView(viewGroup.getChildAt(i)); } } } ``` PhotoViewer(extends ViewPager)を配置したPhotoViewerFragmentのレイアウト ``` ``` PhotoViewerで表示させているPhotoFragmentのレイアウト ``` ``` 追記 クラッシュした時のヒープダンプをMATで調べてみるとbyte[]が一番メモリを消費しているようです。 画像のメモリ解放処理が甘いのでしょうか? ![クラッシュした時のヒープHistgram](https://i.stack.imgur.com/5IE3Y.png) 追記2 @yamacraftさんのおっしゃる通りViewPagerのキャッシュ指定は特にしていないのでデフォルトの1だと思います。 Fragmentの参照が生きているのでは?という話ですが、自パッケージのオブジェクトについて見てみると、その`Fragment`に配置している`AddCartButton`が5つ・`ScalingPhotoView`のオブジェクトが3つ残っているなどFragmentの参照がいくらか残っているようです。 ですが、以下のページによるとViewPagerは今と左右の3ページ分保持する仕様のようなので、1ページ1つの`AddCartButton`が5つ残っているのは気になりますが、その`Fragment`の参照は問題ではないような気がします。 FragmentPagerAdapterとListFragmentを使ったらはまった – unsweets.log ちなみにその時の自パッケージのオブジェクトShallow HeapTOP3は以下の通りでした。 1. `AddCartButton` - 3,160Byte (オブジェクト数:5) 2. `PhotoViewerToolBar` - 1,536Byte (オブジェクト数:3) 3. `ScalingPhotoView` - 1,392Byte (オブジェクト数:3) 追記3 横縦横縦・・・の写真を閲覧していて6枚目で急にローディングに少し時間がかかり、7枚目は他と変わらないものの、8枚目はメモリ不足になります。 再起動した後などは使用できるメモリがいくらか多かったのか8枚目も見れて9枚目でメモリ不足になったこともありました。 ですが、例えば以下のパターンの場合は多少増減はするものの大した変化は見られず、メモリ不足にならずに50枚全てを閲覧することができます。 写真1 縦向き 8枚 写真2 縦向き 8枚 写真3 縦向き 8枚 写真4 横向き 8枚 写真5 横向き 8枚 写真6 横向き 2枚 写真番号ごとに同じ見た目ですが、50枚それぞれ読み込みURLは異なっています。 50枚目を閲覧した後のメモリダンプを調べてみたところ、byte[]のShallow Heapがメモリ不足になった時の2/3ほどでした。 ![compare](https://i.stack.imgur.com/WiWG9.png) 自作の画像を表示させているViewのオブジェクト数などは、50枚閲覧した方はViewPagerの前のListViewで使っている画像を表示させているViewがないこと以外は変わっていません。 画像によるのか画像の向きが関係しているのか確認してみます。 追記4 私がiPhone6sで撮影した横向きの写真(ファイルサイズ1.6MBほど)とそれをMacのプレビューで縦向きにしたものを用意して試してみました。 横向き - 縦向き - 全て横向きや縦向きの場合は50枚閲覧しても問題なく、縦横交互にすると9枚目でメモリ不足になりました。 また縦横2枚ずつ交互の場合も9枚目、縦横3枚ずつ交互の場合は11枚目でメモリ不足になりました。 もしかしたらこれはViewPagerがらみではなくVolleyがらみの問題でしょうか? 追記5 @yamacraft さん追記ありがとうございます。 キャンセル処理は最初に書いたように`Fragment#onDestroyView`のタイミングでさせているはずなので問題ないはずです。 `//もしタグが残っていたらキャンセル処理をさせておく`の部分です。 読み込みが終わったらタグを消す処理をしているので、`タグが残っている=読み込み処理をまだやっている`と判断してキャンセル処理をさせています。 そもそも次へ移動する操作は写真が表示されてから行っているので、読み込みタスクは残っていないと思っています。 以前写真一覧でメモリ不足に悩まされたことがありましたが、その時は自前で同様のメモリ解放処理を書いたら解決しました。 当時の質問 - 写真を一定量読み込むとメモリ不足になってしまうのですが - byte配列がメモリの大部分を占拠するのを解消したい。 VolleyはAPI23で非推奨のものを使っているのは知りませんでした。 まだ本アプリはまだcompileSdkVersionが22ですが、今回や今後のことも考えて他のライブラリに乗り換えることも前向きに検討してみます。
回答: 6.0.0のソースを10分ほど追いかけてみました。 ・サイズが同じBitmapをキャッシュしている(PoolableImageCache.java) ・デコードのキャンセル処理が見当たらない 以上の情報と再現環境から、このような仮説が立ちます。 ・サイズが違うイメージを何枚か読み込む ・サイズが違うので別々にキャッシュ、また、読み込み時にゴミが大量に発生 ・さらに読み込む(別スレッド)、が、ここでOoM発生 ・キャッシュをパージして、BitmapをGC対象にする ・しかしVM管理外のモジュールで読み込み続けている(メモリを確保し続けている)ので、GCが間に合わず、クラッシュ この仮説が正しいなら、Bitmapキャッシュを切ればいいのですが、API上にはその窓口が 用意されてないようなので手を出せません。 設計を見るに、Volleyのイメージ読み込みは小さなイメージを大量に読むことを想定しているようです。 ソースもいじれませんし、Volleyの利用をやめるしか選択肢がないように思えます。