Out of memory during trimming memory
See original GitHub issueDescription
We receiving OOM crashes from production app. Their appear while we’re trying to trim memory. Our implementation are the same as described here: https://github.com/facebook/fresco/issues/2136#issuecomment-397608264
Stack trace:
Fatal Exception: java.lang.OutOfMemoryError: Failed to allocate a 280364296 byte allocation with 8388608 free bytes and 197MB until OOM, max allowed footprint 204217504, growth limit 402653184
at java.util.Arrays.copyOf(Arrays.java:3139)
at java.util.Arrays.copyOf(Arrays.java:3109)
at java.util.ArrayList.grow(ArrayList.java:275)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:249)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:241)
at java.util.ArrayList.add(ArrayList.java:467)
at com.facebook.imagepipeline.cache.CountingMemoryCache.trimExclusivelyOwnedEntries(CountingMemoryCache.java:411)
at com.facebook.imagepipeline.cache.CountingMemoryCache.trim(CountingMemoryCache.java:349)
at com.myapp.utils.FrescoMemoryTrimmableRegistry.trim(FrescoMemoryTrimmableRegistry.java:23)
at com.myapp.MyApp.onTrimMemory(MyApp.java:200)
at android.app.ActivityThread.handleTrimMemory(ActivityThread.java:5440)
at ...
MyApp.java
is our Application class with override of onTrimMemory()
method in the same way as in comment I posted above. FrescoMemoryTrimmableRegistry.java
:
class FrescoMemoryTrimmableRegistry : MemoryTrimmableRegistry {
private val trimmables = LinkedList<MemoryTrimmable>()
override fun registerMemoryTrimmable(trimmable: MemoryTrimmable) {
trimmables.add(trimmable)
}
override fun unregisterMemoryTrimmable(trimmable: MemoryTrimmable) {
trimmables.remove(trimmable)
}
@Synchronized
fun trim(trimType: MemoryTrimType) {
for (trimmable in trimmables) {
trimmable.trim(trimType)
}
}
}
So crash appear sometimes when app in background (46%) and sometimes when in foreground (54%). According to the message of the crash, system need an insane amount of memory to… trim itself. For example:
Samsung Galaxy J7 Prime (3 GB RAM total):
Fatal Exception: java.lang.OutOfMemoryError: Failed to allocate a 420546432 byte allocation with 8388608 free bytes and 231MB until OOM, max allowed footprint 302209776, growth limit 536870912
Huawei Y7 Prime (2019) (3 GB RAM total):
Failed to allocate a 420546432 byte allocation with 8339456 free bytes and 236MB until OOM, max allowed footprint 296859888, growth limit 536870912
HUAWEI Nova 2 Lite (3 GB RAM total):
Failed to allocate a 420546432 byte allocation with 25165824 free bytes and 234MB until OOM, max allowed footprint 316307200, growth limit 536870912
Motorola Moto G (5th gen) (2 GB RAM total):
Failed to allocate a 280364296 byte allocation with 8388608 free bytes and 197MB until OOM, max allowed footprint 204217504, growth limit 402653184
Motorola Moto E5 Play (1 GB RAM total):
Failed to allocate a 186909536 byte allocation with 25149440 free bytes and 125MB until OOM, max allowed footprint 162280960, growth limit 268435456
One more thing I found that as you can see above number of required memory is the same across devices with the same memory: ~178 MB for 1 GB RAM devices, 267 MB for 2 GB RAM devices and 401 MB for 3+ GB RAM devices. Probably it’s coincidence: app attempts to increase the size of the array and due to growth factor it growth with such big steps. And low memory devices give up on step N, 2-GB devices give up on step N+1, and so on.
Reproduction
We’re not able to reproduce this bug on our devices. However it production we have 5% of users who faced with this issue.
Able to reproduce in 100% cases. Please check first comment below for more details.
Additional Information
- Fresco version: 2.1.0
- Platform version: Variety of manufactures and Android OS versions. Distribution corresponds to the distribution of platforms for the application as a whole.
Could be related: issues start to occur in our latest app release. We didn’t change anything related to Fresco (like lib version on way of using Fresco API) except adding one more line:
Fresco.getImagePipeline().evictFromMemoryCache(uri)
We call this in very rare cases (for current moment we have only 92 calls of this).
(Not related since I able to reproduce crash without this line)
Issue Analytics
- State:
- Created 4 years ago
- Comments:12 (1 by maintainers)
Top GitHub Comments
Thanks for the report and investigation. This indeed looks strange. Are you sure there are no huge bitmaps getting cached? Could you please check with Flipper plugin (or by debugging CountingMemoryCache#mCachedEntries) that cached bitmaps are of the reasonable size.
After commenting out
onBitmapReady(bitmap)
call insideonNewResultImpl()
callback crash is gone. It seems that the problem is actually in the wrong work with bitmaps inonBitmapReady(bitmap)
.After moving from
BaseBitmapDataSubscriber
toBaseDataSubscriber
problem is gone. I was able to rework logic in my app and now I’m using Closeable References for passing bitmaps across the app.However, I still have a one last question: how it’s possible that passing single 100 KB Bitmap out from
BaseBitmapDataSubscriber.onNewResultImpl(Bitmap)
to the app in 100% cases will cause OOM due to attempt of allocating hundreds of megabytes of memory during trimming memory in Fresco cache when user moves app to background and then recover it?