Fresco: 使用 Fresco lib 时频繁出现 OOM

创建于 2017-03-15  ·  15评论  ·  资料来源: facebook/fresco

描述

我通过 gradle( 1.1.0 ) 使用 fresco lib 来处理我们应用程序中的所有位图处理,在使用应用程序一段时间后它经常抛出 OOM,我们的应用程序一次处理大量图像,可能有 8-单屏显示10张图片(大小不一,有的覆盖了一半的屏幕,有的只占了50-70dp),所有图片都是使用fresco通过网络加载的,

大多数使用的图片都是带有可滚动组件的,例如 ListView、GridView、RecyclerView,而一些图片是通过我们自己的逻辑回收的,下面是相同的实现。

笔记:

  1. Fresco 使用setDownsampleEnabled初始化
  2. 所有加载的图像都是resized ,虽然我们的一些图像是.png但大多数都是jpeg格式

再生产

崩溃日志

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


[填写:我们如何重现该错误? 如果可能,请提供相关图像的 URL,或示例项目。]

解决方案

[可选:您知道需要做什么来解决这个问题吗? 理想情况下,提供解决此问题的拉取请求。]

附加信息

  • 壁画版本:[1.1.0]
  • 平台版本:【Android 5及以上版本】

这就是我们初始化壁画的方式。

        ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig
                .newBuilder(getApplicationContext())
                .setDownsampleEnabled(true)
                .setBitmapMemoryCacheParamsSupplier(new FrescoCacheParams(activityManager))
                .build();

        Fresco.initialize(getApplicationContext(), imagePipelineConfig);

这就是FrescoCacheParams的定义方式。

public class FrescoCacheParams implements Supplier<MemoryCacheParams> {


    private ActivityManager activityManager;

    public FrescoCacheParams(ActivityManager activityManager) {
        this.activityManager = activityManager;
    }

    <strong i="17">@Override</strong>
    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;
            }
        }
    }
}

这是所有图像的加载方式,其中widthheight是将显示加载图像的实际视图大小。

            ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
                    .setResizeOptions(new ResizeOptions(width, height))
                    .build();
            DraweeController controller = Fresco.newDraweeControllerBuilder()
                    .setOldController(getController())
                    .setImageRequest(request)
                    .build();
            setController(controller);

下面是我们回收逻辑的片段,因为我们有一个AdapterView的自定义实现,我们负责释放资源。

       // 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);

```

我们在所有Drawee's上调用上面的代码,一旦它们移出我们AdapterView上的可视区域,用于在卡片内显示图像

最有用的评论

@nucleartip有趣的事实是,鉴于 OOM 的 Glide 提供了大量的内容,我将所有内容从 Glide 移植到 Fresco。

所有15条评论

@nucleartip ,感谢您提交错误报告!

您是否尝试在 OOM 发生之前捕获内存转储以查看正在使用所有内存的内容?

顺便说一句,对于 Lollipop 及更高版本,您使用的是 maxCacheEntrySize=1,这是尝试完全禁用内存缓存还是仅将其限制为单个项目?

@erikandre大队列以更频繁的速度抛出 OOM,所以我使用低缓存条目只是为了实际上禁用内存缓存。

让我为您转储,但无论如何,您能否为此提出一些解决方法,因为这种 OOM 发生在新一代设备中,例如三星 S7、最新的 Nexus 系列手机等。

@erikandre这里是内存转储的快照,在应用程序因 OOM 崩溃之前拍摄。

screen shot 2017-03-16 at 12 02 24 pm

@nucleartip ,你能和我分享那个内存转储吗?

@erikandre堆转储大小约为 250mb,您更喜欢驱动器还是保管箱?

@nucleartip任何一个都可以正常工作:)

这已经解决了吗? 我有同样的问题。

我通过 Eclipse Memory Analyzer 检查了内存转储。
我也看到了太多 byte[] 。
但是当我使用 GC 路径时,找不到任何引用。
似乎这些是从本地引用的,例如 NativePooledByteBuffer 或 NativeMemoryChunk。
我认为那些在 com.facebook.common.references.SharedReference.sLiveObjects 中,但我没有证据。
我不知道为什么那些没有被释放......
我无法解决这个问题。

我也遇到了同样的问题,以下是异常信息:

E/AndroidRuntime: FATAL EXCEPTION: Thread-5458 Process:
    package-name, PID: 4877
    java.lang.OutOfMemoryError: Failed to allocate a 2560012 byte allocation with 1782384 free bytes and 1740KB until OOM
    at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
    at android.graphics.Bitmap.nativeCreate(Native Method)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:843)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:820)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:787)
    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:260)
    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:186)
    at com.facebook.imagepipeline.decoder.DefaultImageDecoder$1.decode(DefaultImageDecoder.java:63)
    at com.facebook.imagepipeline.decoder.DefaultImageDecoder.decode(DefaultImageDecoder.java:126)
    at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder.doDecode(DecodeProducer.java:240)
    at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder.access$200(DecodeProducer.java:112)
    at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder$1.run(DecodeProducer.java:145)
    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)

这是OOM发生前的所有GC信息:

I/art: Starting a blocking GC Alloc
I/art: Starting a blocking GC Alloc
I/art: Alloc sticky concurrent mark sweep GC freed 92215(9MB) AllocSpace objects, 36(660KB) LOS objects, 8% free, 117MB/128MB, paused 1.228ms total 37.123ms
I/art: Starting a blocking GC Alloc
I/art: Starting a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
I/art: Clamp target GC heap from 188MB to 128MB
I/art: Alloc partial concurrent mark sweep GC freed 45395(2MB) AllocSpace objects, 43(860KB) LOS objects, 3% free, 124MB/128MB, paused 727us total 59.648ms
I/art: WaitForGcToComplete blocked for 25.090ms for cause Alloc
I/art: Starting a blocking GC Alloc
I/art: Starting a blocking GC Alloc
I/art: Starting a blocking GC Alloc
W/art: Suspending all threads took: 5.155ms
I/art: Waiting for a blocking GC Alloc
I/art: Alloc sticky concurrent mark sweep GC freed 2803(277KB) AllocSpace objects, 2(40KB) LOS objects, 0% free, 126MB/128MB, paused 579us total 19.249ms
I/art: Starting a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
I/art: Alloc sticky concurrent mark sweep GC freed 822(87KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 126MB/128MB, paused 547us total 12.316ms
I/art: WaitForGcToComplete blocked for 11.410ms for cause Alloc
I/art: Starting a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
I/art: Clamp target GC heap from 190MB to 128MB
I/art: Alloc partial concurrent mark sweep GC freed 4725(241KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 126MB/128MB, paused 658us total 46.759ms
I/art: WaitForGcToComplete blocked for 46.699ms for cause Alloc
I/art: Starting a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
I/art: Clamp target GC heap from 190MB to 128MB
I/art: Alloc concurrent mark sweep GC freed 801(78KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 126MB/128MB, paused 796us total 40.098ms
I/art: WaitForGcToComplete blocked for 35.773ms for cause Alloc
I/art: Starting a blocking GC Alloc
I/art: Forcing collection of SoftReferences for 2MB allocation
I/art: Waiting for a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
I/art: Clamp target GC heap from 191MB to 128MB
I/art: Alloc concurrent mark sweep GC freed 3661(366KB) AllocSpace objects, 1(20KB) LOS objects, 0% free, 127MB/128MB, paused 634us total 61.758ms
I/art: WaitForGcToComplete blocked for 60.363ms for cause Alloc
I/art: Starting a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
W/art: Suspending all threads took: 38.816ms
/I/art: Forcing collection of SoftReferences for 2MB allocation
/I/art: Waiting for a blocking GC Alloc
/I/art: Clamp target GC heap from 190MB to 128MB
/I/art: Alloc concurrent mark sweep GC freed 214(8KB) AllocSpace objects, 1(416KB) LOS objects, 0% free, 126MB/128MB, paused 876us total 42.655ms
/I/art: WaitForGcToComplete blocked for 82.057ms for cause Alloc
/I/art: Starting a blocking GC Alloc
/I/art: Starting a blocking GC Alloc
/W/art: Throwing OutOfMemoryError "Failed to allocate a 2560012 byte allocation with 1643888 free bytes and 1605KB until OOM"
/I/art: Waiting for a blocking GC Alloc
/I/art: Clamp target GC heap from 190MB to 128MB
/I/art: Alloc concurrent mark sweep GC freed 12633(1496KB) AllocSpace objects, 0(0B) LOS objects, 1% free, 126MB/128MB, paused 684us total 43.271ms
/I/art: WaitForGcToComplete blocked for 136.549ms for cause Alloc
/I/art: Starting a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc
/I/art: Forcing collection of SoftReferences for 2MB allocation
/I/art: Waiting for a blocking GC Alloc
/I/art: Clamp target GC heap from 190MB to 128MB
/I/art: Alloc concurrent mark sweep GC freed 2239(265KB) AllocSpace objects, 0(0B) LOS objects, 1% free, 126MB/128MB, paused 1.282ms total 40.182ms
/I/art: WaitForGcToComplete blocked for 120.837ms for cause Alloc
/I/art: Starting a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc
/I/art: Forcing collection of SoftReferences for 2MB allocation
/I/art: Waiting for a blocking GC Alloc
/W/art: Suspending all threads took: 30.572ms
/I/art: Clamp target GC heap from 190MB to 128MB
/I/art: Alloc concurrent mark sweep GC freed 7(1264B) AllocSpace objects, 0(0B) LOS objects, 1% free, 126MB/128MB, paused 2.697ms total 43.668ms
/I/art: WaitForGcToComplete blocked for 129.040ms for cause Alloc
/I/art: Starting a blocking GC Alloc
/I/art: WaitForGcToComplete blocked for 83.495ms for cause Alloc
/I/art: Starting a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc
/W/art: Throwing OutOfMemoryError "Failed to allocate a 2560012 byte allocation with 1782384 free bytes and 1740KB until OOM"
/I/art: Waiting for a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc

1 崩溃前,在第一个打开一个Viewpager + RecyclerView页面:
1

大约有20张图片。

2 崩溃前,在前两个打开一个Viewpager + RecyclerView页面:
2

大约有40张图片,Pager两侧的Viewpager各有30张图片。

3 崩溃后:
3

我越来越频繁地看到这种情况,尤其是在具有更多可用 RAM 的较新设备上。 就好比 RAM Fresco 必须使用的内存越多,OOM 的频率就越高。

我的配置是默认配置(还没有尝试实现我自己的供应商),但我严重怀疑这会有多大帮助(除非我将最大缓存条目设置为 0,从而完全禁用缓存)。

知道我们如何解决这个问题吗?

@mradzinski我最终完全移植到Glide ,从而宣布我们的应用程序不受 OOM 的影响。

@nucleartip有趣的事实是,鉴于 OOM 的 Glide 提供了大量的内容,我将所有内容从 Glide 移植到 Fresco。

关闭此信息,因为我们没有足够的信息来继续调查。
对于任何其他内存不足问题,请创建一个新任务并最好包含 HPROF 内存转储。

@erikandre

我知道您没有足够的信息来解决 OOM 问题。 我只是建议使用回调发布错误事件,而不是仅仅抛出 OOM 错误。 这样我们就可以自己处理OOM事件了。

@AlexCJW我不认为有一种方法可以处理系统引发的异常(例如 OOM),而且无法想到您的应用程序能够在收到 OOM 后响应回调的场景,因为该回调毕竟,它本身也必须消耗不可用的内存。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

qiiyue picture qiiyue  ·  4评论

amodkanthe picture amodkanthe  ·  3评论

sungerk picture sungerk  ·  3评论

bigfreeZhou picture bigfreeZhou  ·  4评论

kingty picture kingty  ·  4评论