JNI reference table overflow in Android port
essoft

I recently got my project to build with the Android port of Allegro and I have had some problems with the JNI reference table overflowing when destroying and loading bitmaps.

To make sure it wasn't caused by something else I was doing I did a simple test by adding the following to the main loop of the Allegro Android demo (just before the alexlogo.png bitmap is drawn) to see if it would do the same thing:

al_destroy_bitmap(image);
image = al_load_bitmap("alexlogo.png");

This also causes it to crash, with the following error:

#SelectExpand
1E/dalvikvm( 3143): JNI ERROR (app bug): local reference table overflow (max=512) 2W/dalvikvm( 3143): JNI local reference table (0x57c58268) dump: 3W/dalvikvm( 3143): Last 10 entries (of 512): 4W/dalvikvm( 3143): 511: 0x4188b800 android.graphics.Bitmap$Config 5W/dalvikvm( 3143): 510: 0x42cc81b8 android.graphics.BitmapFactory$Options 6W/dalvikvm( 3143): 509: 0x418b4670 java.lang.Class<android.graphics.BitmapFactory> 7W/dalvikvm( 3143): 508: 0x42131428 java.lang.Class<uk.co.essoft.adliti.AllegroActivity> 8W/dalvikvm( 3143): 507: 0x42cc8168 java.lang.String "alexlogo.png" 9W/dalvikvm( 3143): 506: 0x42125c98 android.graphics.Bitmap 10W/dalvikvm( 3143): 505: 0x42131428 java.lang.Class<uk.co.essoft.adliti.AllegroActivity> 11W/dalvikvm( 3143): 504: 0x42131428 java.lang.Class<uk.co.essoft.adliti.AllegroActivity> 12W/dalvikvm( 3143): 503: 0x42124788 android.graphics.Bitmap 13W/dalvikvm( 3143): 502: 0x42131428 java.lang.Class<uk.co.essoft.adliti.AllegroActivity> 14W/dalvikvm( 3143): Summary: 15W/dalvikvm( 3143): 340 of java.lang.Class (2 unique instances) 16W/dalvikvm( 3143): 1 of java.lang.String 17W/dalvikvm( 3143): 169 of android.graphics.Bitmap (169 unique instances) 18W/dalvikvm( 3143): 1 of android.graphics.Bitmap$Config 19W/dalvikvm( 3143): 1 of android.graphics.BitmapFactory$Options 20E/dalvikvm( 3143): Failed adding to JNI local ref table (has 512 entries)

I found that in android/jni_helpers.c the local reference to the class_id in _jni_callObjectMethodV() doesn't seem to be deleted, so I added the following line to the end of the function and recompiled Allegro:

_jni_callv(env, DeleteLocalRef, class_id);

This change allows it to run a little longer, but it then crashes due to references to android.graphics.Bitmap filling up the reference table, which produces the following error:

#SelectExpand
1E/dalvikvm( 3708): JNI ERROR (app bug): local reference table overflow (max=512) 2W/dalvikvm( 3708): JNI local reference table (0x57c52198) dump: 3W/dalvikvm( 3708): Last 10 entries (of 512): 4W/dalvikvm( 3708): 511: 0x42124980 java.lang.String "image/png" 5W/dalvikvm( 3708): 510: 0x444a89d8 byte[] (65536 elements) 6W/dalvikvm( 3708): 509: 0x4188b800 android.graphics.Bitmap$Config 7W/dalvikvm( 3708): 508: 0x421248d8 android.graphics.BitmapFactory$Options 8W/dalvikvm( 3708): 507: 0x418b4670 java.lang.Class<android.graphics.BitmapFactory> 9W/dalvikvm( 3708): 506: 0x42124818 android.graphics.Bitmap 10W/dalvikvm( 3708): 505: 0x42124668 android.graphics.Bitmap 11W/dalvikvm( 3708): 504: 0x421244b8 android.graphics.Bitmap 12W/dalvikvm( 3708): 503: 0x42124308 android.graphics.Bitmap 13W/dalvikvm( 3708): 502: 0x42124158 android.graphics.Bitmap 14W/dalvikvm( 3708): Summary: 15W/dalvikvm( 3708): 2 of java.lang.Class (2 unique instances) 16W/dalvikvm( 3708): 2 of java.lang.String (2 unique instances) 17W/dalvikvm( 3708): 1 of byte[] (65536 elements) 18W/dalvikvm( 3708): 505 of android.graphics.Bitmap (505 unique instances) 19W/dalvikvm( 3708): 1 of android.graphics.Bitmap$Config 20W/dalvikvm( 3708): 1 of android.graphics.BitmapFactory$Options 21E/dalvikvm( 3708): Failed adding to JNI local ref table (has 512 entries)

I could not figure out how to delete the references to the bitmap though. I found the JNI method that loads the images in android_system.c - but it seems to already be deleting the reference to jbitmap, so I'm not sure why this is happening.

Any ideas how this can be fixed, or is it something I am doing wrong?

Thanks.

Thomas Fjellstrom

We've had issues with that in the past. It's tricky to get right, and at one point I think android itself wasn't properly freeing references (I think, not 100% sure and it was a long time ago).

I think we need to go in an do an audit on all our jni references. It's very important for us to free them all properly.

We might also want to try and increase that number if possible..

Trent Gamblin

What version of Allegro are you using? I'm not sure it's fixed even in the latest but I did find this problem myself and tried to fix it but didn't test it. I personally use the non-native loaders (libpng, libjpeg) instead so I don't test this part of the code and I was actually waiting for someone to have this problem.

I'm guessing that since you built the native loader you're not using the latest version of Allegro -- unless of course you manually enabled it, because it's off by default now. Let me know, I'd like to fix it but I need this information first. Also if you're NOT using the latest Allegro (from git) then try that and see if it fixes it (enable the native image loader, which may require editing a CMakeLists.txt because of this problem I wasn't sure is fixed or not.)

essoft

I am currently using the 5.1.7 release of Allegro. I will try running it with the latest version from Git later and let you know the result.

Trent Gamblin

Yeah, let us know how it goes. bump

essoft

I have just tried this on the latest Allegro from the Git repository. The class_id local reference is being deleted now, but unfortunately it still crashes due to the android.graphics.Bitmap references filling up the reference table.

Trent Gamblin

I found a certain bug, I think this is the cause. Please try this patch:

#SelectExpand
1diff --git a/src/android/android_system.c b/src/android/android_system.c 2index a233897..cbd24fc 100644 3--- a/src/android/android_system.c 4+++ b/src/android/android_system.c 5@@ -720,12 +720,13 @@ ALLEGRO_BITMAP *_al_android_load_image(const char *filename, int flags) 6 bitmap = al_create_bitmap(bitmap_w, bitmap_h); 7 al_restore_state(&state); 8 if(!bitmap) { 9+ _jni_callv(jnienv, DeleteLocalRef, jbitmap); 10 return NULL; 11 } 12 13 ALLEGRO_LOCKED_REGION *lr = al_lock_bitmap(bitmap, ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE, ALLEGRO_LOCK_WRITEONLY); 14 15- jintArray ia = jbitmap = _jni_callObjectMethodV(jnienv, system_data.activity_object, "getPixels", "(Landroid/graphics/Bitmap;)[I", jbitmap); 16+ jintArray ia = _jni_callObjectMethodV(jnienv, system_data.activity_object, "getPixels", "(Landroid/graphics/Bitmap;)[I", jbitmap); 17 jint *arr = (*jnienv)->GetIntArrayElements(jnienv, ia, 0); 18 uint32_t *src = (uint32_t *)arr; 19 uint32_t c;

Thomas Fjellstrom

Was that pulled out of my original code? If so, I am so so sorry for that :-x

Trent Gamblin

I don't know but stuff happens. More likely it was me. :P

Thomas Fjellstrom

I dunno, I think that the native image loader code was moved at one point, git blame says that code is you, but I thought I wrote most of it. and it looks like something I might do when tired.

essoft

With your patch the references to android.graphics.Bitmap are fixed, however references to int[] still overflowed the reference table. I made the following additional change to fix that and it now works :) Thanks.

#SelectExpand
1@@ -747,6 +748,7 @@ ALLEGRO_BITMAP *_al_android_load_image(const char *filename, int flags) 2 } 3 } 4 (*jnienv)->ReleaseIntArrayElements(jnienv, ia, arr, JNI_ABORT); 5+ _jni_callv(jnienv, DeleteLocalRef, ia); 6 7 // tell java we're done with the bitmap as well 8 _jni_callv(jnienv, DeleteLocalRef, jbitmap);

Out of interest, would you normally recommend using libpng/libjpeg instead of the internal loaders (since this is now the default), or does it not make much difference?

Thomas Fjellstrom

I'm not entirely sure what is better, technically using the native loaders theres a possibility of hw decoding, and the OS includes the memory in your app's usage (on newer versions of android), where as C+malloc is counted against the OS/kernel itself IIRC, and it has a limited amount of memory and can potentially cause stability issues I think.

I would give both a shot and use which ever works best.

Trent Gamblin

Thanks for the patch. I would thoroughly test the native loader (load and destroy many bitmaps over a long period) before relying on it. If it works though, then the difference between the two is really minor and more a matter of taste. I personally have been using the loadpng loader due to this bug (and not knowing exactly how to fix it at first), and I can say that it is stable. The advantages for the native loader are 1) you don't have to compile any extra libraries, and 2) you don't have the "baggage" of extra libraries around which amounts to a tiny bit smaller code size I suppose.

P.S.: If you've updated the build system to add the native loader back to the Android port, I would like to have that code as well to commit to git.

Thanks again!

essoft

No problem. I think I will keep using the native image loaders for now then and see if I have any other problems with it.

To add the native loader back to the Android port I just copied the following back in from the 5.1.7 release:

#SelectExpand
1diff --git a/addons/image/CMakeLists.txt b/addons/image/CMakeLists.txt 2index 2ef4863..ef0eb73 100644 3--- a/addons/image/CMakeLists.txt 4+++ b/addons/image/CMakeLists.txt 5@@ -66,6 +66,13 @@ if(WANT_NATIVE_IMAGE_LOADER) 6 list(APPEND IMAGE_SOURCES macosx.m) 7 endif(MACOSX) 8 9+ if(ANDROID) 10+ set(ALLEGRO_CFG_IIO_HAVE_ANDROID 1) 11+ set(ALLEGRO_CFG_IIO_SUPPORT_JPG 1) 12+ set(ALLEGRO_CFG_IIO_SUPPORT_PNG 1) 13+ list(APPEND IMAGE_SOURCES android.c) 14+ endif(ANDROID) 15+ 16 if(IPHONE) 17 list(APPEND IMAGE_SOURCES iphone.m) 18 endif(IPHONE)

Trent Gamblin

Thanks again. All changes have been committed to git.

If you have any other problems on Android, let us know! :)

Thread #613397. Printed from Allegro.cc