我通过 gradle( 1.1.0
) 使用 fresco lib 来处理我们应用程序中的所有位图处理,在使用应用程序一段时间后它经常抛出 OOM,我们的应用程序一次处理大量图像,可能有 8-单屏显示10张图片(大小不一,有的覆盖了一半的屏幕,有的只占了50-70dp),所有图片都是使用fresco通过网络加载的,
大多数使用的图片都是带有可滚动组件的,例如 ListView、GridView、RecyclerView,而一些图片是通过我们自己的逻辑回收的,下面是相同的实现。
笔记:
setDownsampleEnabled
初始化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,或示例项目。]
[可选:您知道需要做什么来解决这个问题吗? 理想情况下,提供解决此问题的拉取请求。]
这就是我们初始化壁画的方式。
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;
}
}
}
}
这是所有图像的加载方式,其中width
和height
是将显示加载图像的实际视图大小。
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 发生之前捕获内存转储以查看正在使用所有内存的内容?
顺便说一句,对于 Lollipop 及更高版本,您使用的是 maxCacheEntrySize=1,这是尝试完全禁用内存缓存还是仅将其限制为单个项目?
@erikandre大队列以更频繁的速度抛出 OOM,所以我使用低缓存条目只是为了实际上禁用内存缓存。
让我为您转储,但无论如何,您能否为此提出一些解决方法,因为这种 OOM 发生在新一代设备中,例如三星 S7、最新的 Nexus 系列手机等。
@erikandre这里是内存转储的快照,在应用程序因 OOM 崩溃之前拍摄。
@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页面:
大约有20张图片。
2 崩溃前,在前两个打开一个Viewpager + RecyclerView页面:
大约有40张图片,Pager两侧的Viewpager各有30张图片。
3 崩溃后:
我越来越频繁地看到这种情况,尤其是在具有更多可用 RAM 的较新设备上。 就好比 RAM Fresco 必须使用的内存越多,OOM 的频率就越高。
我的配置是默认配置(还没有尝试实现我自己的供应商),但我严重怀疑这会有多大帮助(除非我将最大缓存条目设置为 0,从而完全禁用缓存)。
知道我们如何解决这个问题吗?
@mradzinski我最终完全移植到Glide
,从而宣布我们的应用程序不受 OOM 的影响。
@nucleartip有趣的事实是,鉴于 OOM 的 Glide 提供了大量的内容,我将所有内容从 Glide 移植到 Fresco。
关闭此信息,因为我们没有足够的信息来继续调查。
对于任何其他内存不足问题,请创建一个新任务并最好包含 HPROF 内存转储。
@erikandre
我知道您没有足够的信息来解决 OOM 问题。 我只是建议使用回调发布错误事件,而不是仅仅抛出 OOM 错误。 这样我们就可以自己处理OOM事件了。
@AlexCJW我不认为有一种方法可以处理系统引发的异常(例如 OOM),而且无法想到您的应用程序能够在收到 OOM 后响应回调的场景,因为该回调毕竟,它本身也必须消耗不可用的内存。
最有用的评论
@nucleartip有趣的事实是,鉴于 OOM 的 Glide 提供了大量的内容,我将所有内容从 Glide 移植到 Fresco。