[TIMOB-24379] Android: Image Views not releasing memory, resulting in crash
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | None |
Status | Open |
Resolution | Unresolved |
Affected Version/s | n/a |
Fix Version/s | n/a |
Components | Android |
Labels | n/a |
Reporter | Hans Knöchel |
Assignee | Joshua Quick |
Created | 2017-02-07T13:22:11.000+0000 |
Updated | 2020-10-26T18:07:47.000+0000 |
Description
I'm running into an issue when testing a fullscreen-photo in my ["Studentenfutter" sample app](https://github.com/hansemannn/studentenfutter-app). It works the first time, displays a black/blank background on the second time and crashes on the third time:
[DEBUG] Window: Checkpoint: postWindowCreated()
[INFO] art: Clamp target GC heap from 103MB to 96MB
[INFO] art: Alloc partial concurrent mark sweep GC freed 579(33KB) AllocSpace objects, 1(16KB) LOS objects, 8%% free, 87MB/96MB, paused 373us total 8.788ms
[INFO] art: Clamp target GC heap from 103MB to 96MB
[INFO] art: Alloc concurrent mark sweep GC freed 264(23KB) AllocSpace objects, 0(0B) LOS objects, 8%% free, 87MB/96MB, paused 178us total 10.782ms
[INFO] art: Forcing collection of SoftReferences for 20MB allocation
[INFO] art: Clamp target GC heap from 103MB to 96MB
[INFO] art: Alloc concurrent mark sweep GC freed 3(144B) AllocSpace objects, 0(0B) LOS objects, 8%% free, 87MB/96MB, paused 126us total 9.588ms
[ERROR] art: Throwing OutOfMemoryError "Failed to allocate a 21313932 byte allocation with 8775660 free bytes and 8MB until OOM"
[DEBUG] skia: --- decoder->decode returned false
[ERROR] TiDrawableReference: (pool-3-thread-1) [34,11127] Unable to load bitmap. Not enough memory: Failed to allocate a 21313932 byte allocation with 8775660 free bytes and 8MB until OOM
[ERROR] TiDrawableReference: java.lang.OutOfMemoryError: Failed to allocate a 21313932 byte allocation with 8775660 free bytes and 8MB until OOM
[ERROR] TiDrawableReference: at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
[ERROR] TiDrawableReference: at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
[ERROR] TiDrawableReference: at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:635)
[ERROR] TiDrawableReference: at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:611)
[ERROR] TiDrawableReference: at org.appcelerator.titanium.view.TiDrawableReference.getBitmap(TiDrawableReference.java:348)
[ERROR] TiDrawableReference: at org.appcelerator.titanium.view.TiDrawableReference.getBitmap(TiDrawableReference.java:300)
[ERROR] TiDrawableReference: at org.appcelerator.titanium.util.TiLoadImageManager$LoadImageJob.run(TiLoadImageManager.java:128)
[ERROR] TiDrawableReference: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
[ERROR] TiDrawableReference: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
[ERROR] TiDrawableReference: at java.lang.Thread.run(Thread.java:818)
[INFO] art: Clamp target GC heap from 103MB to 96MB
[INFO] art: Explicit concurrent mark sweep GC freed 68(19KB) AllocSpace objects, 1(16KB) LOS objects, 8%% free, 87MB/96MB, paused 191us total 8.591ms
[WARN] EGL_emulation: eglSurfaceAttrib not implemented
[WARN] OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xdf0a7f60, error=EGL_SUCCESS
[DEBUG] skia: --- SkImageDecoder::Factory returned null
[DEBUG] skia: --- SkImageDecoder::Factory returned null
[DEBUG] skia: --- SkImageDecoder::Factory returned null
[DEBUG] skia: --- SkImageDecoder::Factory returned null
[INFO] art: Alloc partial concurrent mark sweep GC freed 2075(130KB) AllocSpace objects, 4(20MB) LOS objects, 19%% free, 67MB/83MB, paused 175us total 9.134ms
[INFO] art: Alloc concurrent mark sweep GC freed 7(192B) AllocSpace objects, 0(0B) LOS objects, 19%% free, 67MB/83MB, paused 117us total 8.259ms
[INFO] art: Forcing collection of SoftReferences for 40MB allocation
[INFO] art: Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 19%% free, 67MB/83MB, paused 131us total 7.863ms
[ERROR] art: Throwing OutOfMemoryError "Failed to allocate a 42627852 byte allocation with 16777216 free bytes and 28MB until OOM"
[DEBUG] skia: --- decoder->decode returned false
[WARN] EGL_emulation: eglSurfaceAttrib not implemented
[WARN] OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xdf0aa920, error=EGL_SUCCESS
[DEBUG] Window: Window is closed normally.
The image it is trying to load is [this one](http://api.studentenfutter-os.de//_Resources//Persistent//e0132881499cbd44e5b7ef68f9427a55548c85ba//01486472627.jpeg)
To reproduce, just clone the repo and run the app in the Simulator.
[~hansknoechel] I cannot reproduce using your app. I even tried to load a 23MB (7016x9933) JPEG in ImageView which worked fine.
Did you open the window multiple times? And please note I've only tested on the Simulator, so it might only happen to devices with low RAM.
Hey [~jquick], could this also be related to your ticket TIMOB-24528?
The issue here is that Titanium is not calling Bitmap.recycle() on the ImageView's drawable bitmap when we're done using it. Calling the Java Bitmap.recycle() method will immediately delete the bitmap from memory on the C/C++ side (images are actually loaded via libpng and libjpeg on Android). As it is now, we have to wait for the Java garbage collector to recycle it for us. The solution for [TIMOB-24528] will help apps load very large images that are too large to be displayed by the GPU or UI. It won't solve OutOfMemory exception issues (but can help reduce it in certain situations). Since the referenced app displays camera shots, this will help the app display images on certain Android devices that always fail to display the taken photo (ie: where the camera megapixel count exceeds the GPU's max texture size).
[~jquick] Is there a workaround for this btw? I really would love to show full-screen images in my canteen app. It actually crashes with remote images, I am not sure this was clear so far.
You should be able to work-around it via hyperloop if you're willing. Part of the problem here is that we have "TiImageLruCache" which keeps hard references to previously displayed images such as photos. We should get rid of our LRU cache in the future since it's not our job to decide which images should remain in memory. It's the Titanium developer's job to do that... and they can retain the references in a portable way via blobs in JavaScript. But for now, we need to trigger the Java code to clear the LRU cache before loading the next photo. So, what you need to do in hyperloop is call our
TiAppllication.onLowMemory()
method. That will clear the LRU cache. https://github.com/appcelerator/titanium_mobile/blob/master/android/titanium/src/java/org/appcelerator/titanium/TiApplication.java#L390 You can get a hold of aTiApplication
instance by fetching the current activity and calling the JavaActivity.getApplication()
method. OurTiApplication
Java class inherits fromApplication
. https://developer.android.com/reference/android/app/Activity.html#getApplication()Unfortunately it crashes with the 1st image already. And interestingly, the scrollview in which the Image View of my App is embedded in takes like 6s to process before displaying.
If it's with the 1st image, then that's a different issue. Is it blocking for 6 seconds or is it still working async? Do you have a stack trace? (Or is the trace on the ticket the error you're running in to?) Also, I know my refactored image handling from last year (PR below) is better at this. Would you mind giving it a try if possible (might be too old to deal with; so I can understand if you don't). https://github.com/appcelerator/titanium_mobile/pull/8951