Skip to content

fix(android): fix memory error in imageAsResized #14233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 6, 2025

Conversation

m1ga
Copy link
Contributor

@m1ga m1ga commented May 30, 2025

reverts #14156

more info and test code in #14232

@hansemannn hansemannn requested a review from prashantsaini1 June 4, 2025 13:04
Copy link
Contributor

@prashantsaini1 prashantsaini1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m1ga The issue you have tagged with this PR seems that it was not return 0 for recycled bitmaps which caused that issue, but some incorrect calculations for sizeOf. I believe this should fix that error as well because it:

  1. Considers bitmap memory reuse across decode/encode operations, especially for BitmapFactory.Options which is used in imageAsResized and other similar methods.
  2. Gives the actual memory footprint of a Bitmap in RAM.
  3. Already returns 0 for recycled bitmaps.
@Override
protected int sizeOf(String key, Bitmap bitmap)
{
    // Divide by 1024 to keep both the maxSize and sizeOf in KB.
    return bitmap.getAllocationByteCount() / 1024;
}

@m1ga
Copy link
Contributor Author

m1ga commented Jun 6, 2025

sadly it doesn't work with that code change. Here is the output with the test code from the issue :

selecting 1. image:
[INFO]  file:///data/user/0/com.miga.test/cache/.titanium/tmp/test.png

selecting 2. image:
[INFO]  file:///data/user/0/com.miga.test/cache/.titanium/tmp/test.png

selecting 3. image:
[WARN]  W/com.miga.test: Cleared Reference was only reachable from finalizer (only reported once)
[WARN]  Bitmap: Called getAllocationByteCount() on a recycle()'d bitmap! This is undefined behavior!
[WARN]  Bitmap: Called getAllocationByteCount() on a recycle()'d bitmap! This is undefined behavior!
[INFO]  file:///data/user/0/com.miga.test/cache/.titanium/tmp/test.png

selecting 4. image:
[WARN]  Bitmap: Called getAllocationByteCount() on a recycle()'d bitmap! This is undefined behavior!
[WARN]  Bitmap: Called getAllocationByteCount() on a recycle()'d bitmap! This is undefined behavior!
[ERROR] TiBlob: (main) [17462,17562] Unable to resize the image. Unknown exception: org.appcelerator.titanium.util.TiBlobLruCache.sizeOf() is reporting inconsistent results!
[ERROR] TiBlob: java.lang.IllegalStateException: org.appcelerator.titanium.util.TiBlobLruCache.sizeOf() is reporting inconsistent results!
[ERROR] TiBlob:         at androidx.collection.LruCache.trimToSize(LruCache.java:173)
[ERROR] TiBlob:         at androidx.collection.LruCache.put(LruCache.java:156)
[ERROR] TiBlob:         at org.appcelerator.titanium.TiBlob.imageAsResized(TiBlob.java:742)
[ERROR] TiBlob:         at org.appcelerator.kroll.runtime.v8.V8Function.nativeInvoke(Native Method)
[ERROR] TiBlob:         at org.appcelerator.kroll.runtime.v8.V8Function.callSync(V8Function.java:55)
[ERROR] TiBlob:         at org.appcelerator.kroll.runtime.v8.V8Function.call(V8Function.java:41)
[ERROR] TiBlob:         at org.appcelerator.kroll.runtime.v8.V8Function$1.run(V8Function.java:68)
[ERROR] TiBlob:         at android.os.Handler.handleCallback(Handler.java:991)
[ERROR] TiBlob:         at android.os.Handler.dispatchMessage(Handler.java:102)
[ERROR] TiBlob:         at android.os.Looper.loopOnce(Looper.java:232)
[ERROR] TiBlob:         at android.os.Looper.loop(Looper.java:317)
[ERROR] TiBlob:         at android.app.ActivityThread.main(ActivityThread.java:8934)
[ERROR] TiBlob:         at java.lang.reflect.Method.invoke(Native Method)
[ERROR] TiBlob:         at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:591)
[ERROR] TiBlob:         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)

when selecting a 4th image it will start with the error again.

@prashantsaini1
Copy link
Contributor

Did you test it with a fresh installation after deleting the older cache usages?

This time it's thrown at the per-image level, not at the application level (org.appcelerator.titanium.TiApplication.onTrimMemory(TiApplication.java:451)) as mentioned in original issue.

  • This indicates that there's still something wrong with the cacheSize and sizeOf() implementation in TiBlobLruCache.
  • This does not mean that the resized bitmap is not there, it just means that the bitmap failed to be cached separately and the bitmap itself is actually correct.

📝Next Steps:

  • It's a good idea to catch all IllegalStateException thrown by mMemoryCache usages in TiBlob since it's not dependent to the actual Bitmap, at least.
  • I will need to dig deeper into this to find a long term fix.

@m1ga
Copy link
Contributor Author

m1ga commented Jun 6, 2025

yes, fresh installation.

I think it still should be good to revert the old #14156 PR with this one so it's back to the initial behaviour (showing the warning but not crashing) as people reported this on slack and have errors in 12.7.0. Then we should search for the deeper issue

@prashantsaini1
Copy link
Contributor

prashantsaini1 commented Jun 6, 2025

I did a more digging and noticed that the LruCache implementation needs some overhaul. So it's fine to revert for now, but can you please add the handling to catch IllegalStateException on all the mMemoryCache's put/remove usages because it can still throw that exception (in some unexplored scenarios) and would make the apps mis-behave.

With the mMemoryCache exceptions caught, the apps would still keep running as is.

@m1ga
Copy link
Contributor Author

m1ga commented Jun 6, 2025

I've added a general exception catch so it will catch everything.
I've tested it with 8 images in a row and all worked fine (so functionality works, no error or crash but the Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior! warning).

Copy link
Contributor

@prashantsaini1 prashantsaini1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The apps would keep running now (without cached images in these cases), so the warnings are fine. I will try to find the root-cause and provide a fix on this.

Edit: Also, the current cache related code can also be moved to a background thread and make the actual Blob return faster.

@prashantsaini1 prashantsaini1 merged commit ecf24da into master Jun 6, 2025
5 checks passed
@prashantsaini1 prashantsaini1 deleted the androidCacheCrash branch June 6, 2025 13:41
hansemannn pushed a commit that referenced this pull request Jun 8, 2025
* fix(android): fix memory error in imageAsResized

* try/catch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants