frequent OOM while using Fresco lib
See original GitHub issueDescription
Am using fresco lib through gradle(1.1.0
) for all the Bitmaps handling in our application, after using app for a while it frequently throws OOM, our application deals with lots of images at a time, there might be 8-10 images(not all of same sizes, some cover half of screen while some take only 50-70dp in size) displayed in single screen and all of them are loaded through network using fresco,
most of the Images being used are with scrollable components such as ListView,GridView, RecyclerView, while some images are recycled through our own logic, below is implementation for same.
Note:
- Fresco is Initialised using
setDownsampleEnabled
- All the images being loaded are
resized
, though some of our images are of type.png
but most of them are in formatjpeg
Reproduction
Crash LOG
03-15 22:21:21.199 7035-7203/com.xyz E/AndroidRuntime: FATAL EXCEPTION: Thread-442
Process: com.xyz, PID: 7035
java.lang.OutOfMemoryError: Failed to allocate a 1638412 byte allocation with 589552 free bytes and 575KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:831)
at android.graphics.Bitmap.createBitmap(Bitmap.java:808)
at android.graphics.Bitmap.createBitmap(Bitmap.java:775)
at com.facebook.imagepipeline.memory.BitmapPool.alloc(BitmapPool.java:55)
at com.facebook.imagepipeline.memory.BitmapPool.alloc(BitmapPool.java:30)
at com.facebook.imagepipeline.memory.BasePool.get(BasePool.java:259)
at com.facebook.imagepipeline.platform.ArtDecoder.decodeStaticImageFromStream(ArtDecoder.java:137)
at com.facebook.imagepipeline.platform.ArtDecoder.decodeJPEGFromEncodedImage(ArtDecoder.java:120)
at com.facebook.imagepipeline.decoder.DefaultImageDecoder.decodeJpeg(DefaultImageDecoder.java:183)
at com.facebook.imagepipeline.decoder.DefaultImageDecoder$1.decode(DefaultImageDecoder.java:63)
at com.facebook.imagepipeline.decoder.DefaultImageDecoder.decode(DefaultImageDecoder.java:123)
at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder.doDecode(DecodeProducer.java:239)
at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder.access$200(DecodeProducer.java:111)
at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder$1.run(DecodeProducer.java:144)
at com.facebook.imagepipeline.producers.JobScheduler.doJob(JobScheduler.java:207)
at com.facebook.imagepipeline.producers.JobScheduler.access$000(JobScheduler.java:27)
at com.facebook.imagepipeline.producers.JobScheduler$1.run(JobScheduler.java:78)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at com.facebook.imagepipeline.core.PriorityThreadFactory$1.run(PriorityThreadFactory.java:43)
at java.lang.Thread.run(Thread.java:818)
03-15 22:44:53.849 8055-8055/com.xyz E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.xyz, PID: 8055
android.view.InflateException: Binary XML file line #2: Binary XML file line #2: Error inflating class <unknown>
at android.view.LayoutInflater.inflate(LayoutInflater.java:539)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at com.xyz.view.newswipe.view.FbLikesGridView$FbLikesAdapter.onCreateViewHolder(FbLikesGridView.java:195)
at com.xyz.view.newswipe.view.FbLikesGridView$FbLikesAdapter.onCreateViewHolder(FbLikesGridView.java:160)
at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6319)
at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5507)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5392)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5388)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2149)
at android.support.v7.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:556)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1496)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:593)
at android.support.v7.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:170)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3535)
at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:2979)
at android.view.View.measure(View.java:18788)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
at android.view.View.measure(View.java:18788)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:1112)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:632)
at android.view.View.measure(View.java:18788)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
at android.view.View.measure(View.java:18788)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:1112)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:632)
at android.view.View.measure(View.java:18788)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
at android.view.View.measure(View.java:18788)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
at android.view.View.measure(View.java:18788)
at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
at android.view.View.measure(View.java:18788)
at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
at android.view.View.measure(View.java:18788)
at android.view.ViewGroup.measureChildWithMargins(ViewG
[FILL THIS OUT: How can we reproduce the bug? Provide URLs to relevant images if possible, or a sample project.]
Solution
[OPTIONAL: Do you know what needs to be done to address this issue? Ideally, provide a pull request which fixes this issue.]
Additional Information
- Fresco version: [1.1.0]
- Platform version: [ Android version 5 and above]
this is how we are initialising fresco.
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig
.newBuilder(getApplicationContext())
.setDownsampleEnabled(true)
.setBitmapMemoryCacheParamsSupplier(new FrescoCacheParams(activityManager))
.build();
Fresco.initialize(getApplicationContext(), imagePipelineConfig);
this is how class FrescoCacheParams
is defined.
public class FrescoCacheParams implements Supplier<MemoryCacheParams> {
private ActivityManager activityManager;
public FrescoCacheParams(ActivityManager activityManager) {
this.activityManager = activityManager;
}
@Override
public MemoryCacheParams get() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int cacheSize = getMaxCacheSize();
Log.d("####","fresco cache size = " + cacheSize);
return new MemoryCacheParams(cacheSize, 1, 1, 1, 1);
} else {
return new MemoryCacheParams(
getMaxCacheSize(),
256,
Integer.MAX_VALUE,
Integer.MAX_VALUE,
Integer.MAX_VALUE);
}
}
private int getMaxCacheSize() {
final int maxMemory = Math.min(activityManager.getMemoryClass()
* ByteConstants.MB, Integer.MAX_VALUE);
if (maxMemory < 32 * ByteConstants.MB) {
return 4 * ByteConstants.MB;
} else if (maxMemory < 64 * ByteConstants.MB) {
return 6 * ByteConstants.MB;
} else {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD) {
return 8 * ByteConstants.MB;
} else {
return maxMemory / 6;
}
}
}
}
This is how all of the Images are being loaded, where width
and height
are actual size of view which will display loaded images.
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
.setResizeOptions(new ResizeOptions(width, height))
.build();
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setOldController(getController())
.setImageRequest(request)
.build();
setController(controller);
Below are snippet of our recycling logic, as we have a custom implementation of AdapterView
we are taking care of releasing resources.
// calling on each Drawee, once it moves out of visible area
if (getController() != null) {
getController().onDetach();
}
// once a image moves out of display area, we are evicting it out of pipeline as well.
ImagePipeline imagePipeline = Fresco.getImagePipeline();
Uri uri = Uri.parse(url);
imagePipeline.evictFromMemoryCache(uri);
we are calling above code on all the Drawee's
which are used for displaying images within out cards, once they move out of viewable area on our AdapterView
Issue Analytics
- State:
- Created 7 years ago
- Comments:15 (4 by maintainers)
Top GitHub Comments
@nucleartip funny fact is that I come from porting everything from Glide to Fresco given the huge amount of OOM’s Glide gives…
@AlexCJW I don’t think there’s a way of handling an exception raised by the system such as an OOM, and furthermore can’t think of an scenario where your app will be able to respond to a callback after receiving an OOM since that callback itself would also have to consume unavailable memory after all.