changeset 2810:e0ac1ef1c85b

Android: JNI changes to address potential memory/object leaks. While I haven't done any memory leak testing, while researching how to improve performance, it has come to my attention that I should be releasing various objects. Particularly java string related references. Always call DeleteLocalRef() after NewStringUTF() and... Always call ReleaseStringUTFChars() after GetStringUTFChars().
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Tue, 26 Jul 2022 19:40:46 +0000
parents f81e92947f4a
children 30cb5d646267
files android/dw.cpp
diffstat 1 files changed, 204 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/android/dw.cpp	Tue Jul 26 18:07:08 2022 +0000
+++ b/android/dw.cpp	Tue Jul 26 19:40:46 2022 +0000
@@ -78,7 +78,10 @@
 
 jclass _dw_find_class(JNIEnv *env, const char* name)
 {
-    return static_cast<jclass>(env->CallObjectMethod(_dw_class_loader, _dw_class_method, env->NewStringUTF(name)));
+    jstring jname = env->NewStringUTF(name);
+    jclass clazz = static_cast<jclass>(env->CallObjectMethod(_dw_class_loader, _dw_class_method, jname));
+    env->DeleteLocalRef(jname);
+    return clazz;
 }
 
 // Do a quick check if an exception occurred...
@@ -159,7 +162,7 @@
 JNIEXPORT void JNICALL
 Java_org_dbsoft_dwindows_DWindows_dwindowsInit(JNIEnv* env, jobject obj, jstring apppath, jstring appcache, jstring appID)
 {
-    char *arg = strdup(env->GetStringUTFChars((jstring)apppath, nullptr));
+    const char *arg = env->GetStringUTFChars((jstring)apppath, nullptr);
     const char *cache = env->GetStringUTFChars((jstring)appcache, nullptr);
     const char *appid = env->GetStringUTFChars((jstring)appID, nullptr);
     char *home = getenv("HOME");
@@ -185,6 +188,7 @@
     {
         /* Store the passed in path for dw_app_dir() */
         strncpy(_dw_exec_dir, cache, MAX_PATH);
+        env->ReleaseStringUTFChars(appcache, cache);
     }
     /* Store the best path for dw_user_dir() */
     if(home)
@@ -197,10 +201,14 @@
     {
         /* Store our reported Android AppID */
         strncpy(_dw_app_id, appid, _DW_APP_ID_SIZE);
+        env->ReleaseStringUTFChars(appID, appid);
     }
 
     /* Launch the new thread to execute dwmain() */
-    dw_thread_new((void *) _dw_main_launch, arg, 0);
+    dw_thread_new((void *) _dw_main_launch, strdup(arg ? arg : ""), 0);
+
+    if(arg)
+        env->ReleaseStringUTFChars(apppath, arg);
 }
 
 typedef struct _dwsighandler
@@ -308,6 +316,8 @@
                 int vk = DW_POINTER_TO_INT(params[4]), special = DW_POINTER_TO_INT(params[5]);
 
                 retval = keypressfunc(handler->window, ch, (int)vk, special, handler->data, utf8);
+                if(utf8)
+                    free(utf8);
                 break;
             }
                 /* Button press and release event */
@@ -363,8 +373,11 @@
             case _DW_EVENT_ITEM_ENTER:
             {
                 int (*containerselectfunc)(HWND, char *, void *, void *) =(int (* API)(HWND, char *, void *, void *)) handler->signalfunction;
-
-                retval = containerselectfunc(handler->window, (char *)params[1], handler->data, params[7]);
+                char *text = (char *)params[1];
+
+                retval = containerselectfunc(handler->window, text, handler->data, params[7]);
+                if(text)
+                    free(text);
                 break;
             }
                 /* Container context menu event */
@@ -377,6 +390,8 @@
                 int y = DW_POINTER_TO_INT(params[4]);
 
                 retval = containercontextfunc(handler->window, text, x, y, handler->data, user);
+                if(text)
+                    free(text);
                 break;
             }
                 /* Generic selection changed event for several classes */
@@ -397,6 +412,8 @@
                 void *user = params[7];
 
                 retval = treeselectfunc(handler->window, (jobject)params[0], text, handler->data, user);
+                if(text)
+                    free(text);
                 break;
             }
                 /* Set Focus event */
@@ -440,6 +457,8 @@
                 char *result = (char *)params[1];
 
                 retval = htmlresultfunc(handler->window, result ? DW_ERROR_NONE : DW_ERROR_UNKNOWN, result, params[7], handler->data);
+                if(result)
+                    free(result);
                 break;
             }
                 /* HTML changed event */
@@ -449,6 +468,8 @@
                 char *uri = (char *)params[1];
 
                 retval = htmlchangedfunc(handler->window, DW_POINTER_TO_INT(params[3]), uri, handler->data);
+                if(uri)
+                    free(uri);
                 break;
             }
         }
@@ -580,12 +601,18 @@
                                                       jint inta, jint intb, jint intc, jint intd) {
     const char *utf81 = str1 ? env->GetStringUTFChars(str1, nullptr) : nullptr;
     const char *utf82 = str2 ? env->GetStringUTFChars(str2, nullptr) : nullptr;
-    void *params[_DW_EVENT_PARAM_SIZE] = { DW_POINTER(obj2), DW_POINTER(utf81), DW_POINTER(utf82),
+    void *params[_DW_EVENT_PARAM_SIZE] = { DW_POINTER(obj2), DW_POINTER(utf81 ? strdup(utf81) : nullptr),
+                                           DW_POINTER(utf82 ? strdup(utf82) : nullptr),
                                            DW_INT_TO_POINTER(inta), DW_INT_TO_POINTER(intb),
                                            DW_INT_TO_POINTER(intc), DW_INT_TO_POINTER(intd), nullptr,
                                            DW_INT_TO_POINTER(message), nullptr };
 
-    return _dw_event_handler(obj1, params);
+    int retval = _dw_event_handler(obj1, params);
+    if(utf81)
+        env->ReleaseStringUTFChars(str1, utf81);
+    if(utf82)
+        env->ReleaseStringUTFChars(str2, utf82);
+    return retval;
 }
 
 /* A more simple method for quicker calls */
@@ -614,20 +641,26 @@
 Java_org_dbsoft_dwindows_DWindows_eventHandlerHTMLResult(JNIEnv* env, jobject obj, jobject obj1,
                                                jint message, jstring htmlResult, jlong data) {
     const char *result = env->GetStringUTFChars(htmlResult, nullptr);
-    void *params[_DW_EVENT_PARAM_SIZE] = { nullptr, DW_POINTER(result), nullptr, nullptr, nullptr, nullptr, nullptr,
-                                           DW_INT_TO_POINTER(data), DW_INT_TO_POINTER(message), nullptr };
+    void *params[_DW_EVENT_PARAM_SIZE] = { nullptr, DW_POINTER(result ? strdup(result) : nullptr), nullptr,
+                                           nullptr, nullptr, nullptr, nullptr, DW_INT_TO_POINTER(data),
+                                           DW_INT_TO_POINTER(message), nullptr };
 
     _dw_event_handler(obj1, params);
+    if(result)
+        env->ReleaseStringUTFChars(htmlResult, result);
 }
 
 JNIEXPORT void JNICALL
 Java_org_dbsoft_dwindows_DWWebViewClient_eventHandlerHTMLChanged(JNIEnv* env, jobject obj, jobject obj1,
                                                          jint message, jstring URI, jint status) {
     const char *uri = env->GetStringUTFChars(URI, nullptr);
-    void *params[_DW_EVENT_PARAM_SIZE] = { nullptr, DW_POINTER(uri), nullptr, DW_INT_TO_POINTER(status),
-                                           nullptr, nullptr, nullptr, nullptr, DW_INT_TO_POINTER(message), nullptr };
+    void *params[_DW_EVENT_PARAM_SIZE] = { nullptr, DW_POINTER(uri ? strdup(uri) : nullptr), nullptr,
+                                           DW_INT_TO_POINTER(status), nullptr, nullptr, nullptr, nullptr,
+                                           DW_INT_TO_POINTER(message), nullptr };
 
     _dw_event_handler(obj1, params);
+    if(uri)
+        env->ReleaseStringUTFChars(URI, uri);
 }
 
 typedef struct _dwprint
@@ -732,20 +765,26 @@
 Java_org_dbsoft_dwindows_DWindows_eventHandlerContainer(JNIEnv* env, jobject obj, jobject obj1,
                                                   jint message, jstring jtitle, jint x, jint y, jlong data) {
     const char *title = jtitle ? env->GetStringUTFChars(jtitle, nullptr) : nullptr;
-    void *params[_DW_EVENT_PARAM_SIZE] = { nullptr, DW_POINTER(title), nullptr, DW_INT_TO_POINTER(x), DW_INT_TO_POINTER(y),
-                                           nullptr, nullptr, DW_POINTER(data), DW_INT_TO_POINTER(message), nullptr };
+    void *params[_DW_EVENT_PARAM_SIZE] = { nullptr, DW_POINTER(title ? strdup(title) : nullptr), nullptr,
+                                           DW_INT_TO_POINTER(x), DW_INT_TO_POINTER(y), nullptr, nullptr,
+                                           DW_POINTER(data), DW_INT_TO_POINTER(message), nullptr };
 
     _dw_event_handler(obj1, params);
+    if(title)
+        env->ReleaseStringUTFChars(jtitle, title);
 }
 
 JNIEXPORT void JNICALL
 Java_org_dbsoft_dwindows_DWindows_eventHandlerTree(JNIEnv* env, jobject obj, jobject obj1,
                                                    jint message, jobject item, jstring jtitle, jlong data) {
     const char *title = jtitle ? env->GetStringUTFChars(jtitle, nullptr) : nullptr;
-    void *params[_DW_EVENT_PARAM_SIZE] = { DW_POINTER(item), DW_POINTER(title), nullptr, nullptr, nullptr,
-                                           nullptr, nullptr, DW_POINTER(data), DW_INT_TO_POINTER(message), nullptr };
+    void *params[_DW_EVENT_PARAM_SIZE] = { DW_POINTER(item), DW_POINTER(title ? strdup(title) : nullptr),
+                                           nullptr, nullptr, nullptr, nullptr, nullptr, DW_POINTER(data),
+                                           DW_INT_TO_POINTER(message), nullptr };
 
     _dw_event_handler(obj1, params);
+    if(title)
+        env->ReleaseStringUTFChars(jtitle, title);
 }
 
 JNIEXPORT void JNICALL
@@ -761,10 +800,13 @@
 Java_org_dbsoft_dwindows_DWindows_eventHandlerKey(JNIEnv *env, jobject obj, jobject obj1, jint message, jint ch,
                                                   jint vk, jint modifiers, jstring str) {
     const char *cstr = str ? env->GetStringUTFChars(str, nullptr) : nullptr;
-    void *params[_DW_EVENT_PARAM_SIZE] = { nullptr, DW_POINTER(cstr), nullptr, DW_INT_TO_POINTER(ch), DW_INT_TO_POINTER(vk),
-                                           DW_INT_TO_POINTER(modifiers), nullptr, nullptr, DW_INT_TO_POINTER(message), nullptr };
+    void *params[_DW_EVENT_PARAM_SIZE] = { nullptr, DW_POINTER(cstr ? strdup(cstr) : nullptr), nullptr,
+                                           DW_INT_TO_POINTER(ch), DW_INT_TO_POINTER(vk), DW_INT_TO_POINTER(modifiers),
+                                           nullptr, nullptr, DW_INT_TO_POINTER(message), nullptr };
 
     _dw_event_handler(obj1, params);
+    if(cstr)
+        env->ReleaseStringUTFChars(str, cstr);
 }
 
 /* Handler for Timer events */
@@ -1129,6 +1171,7 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, debugMessage, jstr);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
     else {
         /* Output to stderr, if there is another way to send it
@@ -1173,6 +1216,8 @@
         // Call the method on the object
         retval = env->CallIntMethod(_dw_obj, messageBox, jstrtitle, jstr, flags);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
+        env->DeleteLocalRef(jstrtitle);
     }
     return retval;
 }
@@ -1217,6 +1262,11 @@
             if(str)
                 retval = strdup(str);
         }
+        env->DeleteLocalRef(jstr);
+        if(path)
+            env->DeleteLocalRef(path);
+        if(jext)
+            env->DeleteLocalRef(jext);
     }
     return retval;
 }
@@ -1247,6 +1297,7 @@
             // Call the method on the object
             retval = (int) env->CallIntMethod(_dw_obj, fileOpen, jstr, (jint) mode);
             _dw_jni_check_exception(env);
+            env->DeleteLocalRef(jstr);
         }
     }
     return retval;
@@ -1267,6 +1318,7 @@
     if((env = (JNIEnv *)pthread_getspecific(_dw_env_key)))
     {
         const char *utf8 = nullptr;
+        char *retval = nullptr;
 
         // First get the class that contains the method you need to call
         jclass clazz = _dw_find_class(env, DW_CLASS_NAME);
@@ -1278,7 +1330,12 @@
         // Get the UTF8 string result
         if(result)
             utf8 = env->GetStringUTFChars(result, nullptr);
-        return utf8 ? strdup(utf8) : nullptr;
+        if(utf8)
+        {
+            retval = strdup(utf8);
+            env->ReleaseStringUTFChars(result, utf8);
+        }
+        return retval;
     }
     return nullptr;
 }
@@ -1305,6 +1362,7 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, clipboardSetText, jstr);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -1418,6 +1476,8 @@
                                                  "(IILjava/lang/String;)Landroid/widget/LinearLayout;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, groupBoxNew, type, pad, jstr), _DW_REFERENCE_WEAK);
+        if(jstr)
+            env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -1647,6 +1707,7 @@
                                                "(Ljava/lang/String;I)Landroid/widget/Button;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, buttonNew, jstr, (int)cid), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -1667,6 +1728,7 @@
                                                    "(Ljava/lang/String;II)Landroid/widget/EditText;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, entryfieldNew, jstr, (int)cid, password), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -1743,6 +1805,7 @@
                                                      "(Ljava/lang/String;I)Landroid/widget/ImageButton;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, bitmapButtonNew, jstr, (int)resid), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -1767,7 +1830,7 @@
     {
         // Construct a String
         jstring jstr = env->NewStringUTF(text);
-        jstring path = env->NewStringUTF(text);
+        jstring path = env->NewStringUTF(filename);
         // First get the class that contains the method you need to call
         jclass clazz = _dw_find_class(env, DW_CLASS_NAME);
         // Get the method that you want to call
@@ -1775,6 +1838,8 @@
                                                              "(Ljava/lang/String;ILjava/lang/String;)Landroid/widget/ImageButton;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, bitmapButtonNewFromFile, jstr, (int)cid, path), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
+        env->DeleteLocalRef(path);
         return result;
     }
     return nullptr;
@@ -1809,8 +1874,9 @@
                                                              "(Ljava/lang/String;I[BI)Landroid/widget/ImageButton;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, bitmapButtonNewFromData, jstr, (int)cid, bytearray, len), _DW_REFERENCE_WEAK);
-        // Clean up after the array now that we are finished
+        // TODO: Clean up after the array now that we are finished
         //env->ReleaseByteArrayElements(bytearray, (jbyte *) data, 0);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -1839,6 +1905,7 @@
                                                    "(Ljava/lang/String;I)Lorg/dbsoft/dwindows/DWSpinButton;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, spinButtonNew, jstr, (int)cid), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -1941,6 +2008,7 @@
                                                     "(Ljava/lang/String;I)Landroid/widget/RadioButton;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, radioButtonNew, jstr, (int)cid), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -2155,6 +2223,7 @@
                                                  "(Ljava/lang/String;I)Landroid/widget/CheckBox;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, checkboxNew, jstr, (int)cid), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -2258,6 +2327,7 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, listOrComboBoxAppend, handle, jstr);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -2284,6 +2354,7 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, listOrComboBoxInsert, handle, jstr, pos);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -2401,7 +2472,11 @@
         {
             const char *utf8 = env->GetStringUTFChars(result, nullptr);
 
-            strncpy(buffer, utf8, length);
+            if(utf8)
+            {
+                strncpy(buffer, utf8, length);
+                env->ReleaseStringUTFChars(result, utf8);
+            }
         }
     }
 }
@@ -2429,6 +2504,7 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, listOrComboBoxSetText, handle, index, jstr);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -2557,6 +2633,7 @@
                                                  "(Ljava/lang/String;I)Lorg/dbsoft/dwindows/DWComboBox;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, comboBoxNew, jstr, (int)cid), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -2614,6 +2691,7 @@
         retval = env->CallIntMethod(_dw_obj, mleImport, handle, jstr, startpoint);
         if(_dw_jni_check_exception(env))
             retval = 0;
+        env->DeleteLocalRef(jstr);
     }
     return retval;
 }
@@ -2868,6 +2946,7 @@
         retval = env->CallIntMethod(_dw_obj, mleSearch, handle, jstr, point, (jint)flags);
         if(_dw_jni_check_exception(env))
             retval = DW_ERROR_UNKNOWN;
+        env->DeleteLocalRef(jstr);
     }
     return retval;
 }
@@ -2907,6 +2986,7 @@
                                              "(Ljava/lang/String;II)Landroid/widget/TextView;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, textNew, jstr, (int)cid, status), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -3128,6 +3208,7 @@
                             pixmap ? pixmap->typeface : nullptr, pixmap ? pixmap->fontsize : 0,
                             pixmap ? pixmap->handle : nullptr, fgcolor, bgcolor);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -3162,6 +3243,7 @@
             *width = dimensions & 0xFFFF;
         if(height)
             *height = (dimensions >> 32) & 0xFFFF;
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -3320,6 +3402,7 @@
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, treeInsertAfter, handle, item,
                                                                          jstr, icon, parent, (jlong)itemdata), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -3356,6 +3439,7 @@
     if(handle && item && (env = (JNIEnv *)pthread_getspecific(_dw_env_key)))
     {
         const char *utf8 = nullptr;
+        char *retval = nullptr;
 
         // First get the class that contains the method you need to call
         jclass clazz = _dw_find_class(env, DW_CLASS_NAME);
@@ -3367,7 +3451,11 @@
         // Get the UTF8 string result
         if(result)
             utf8 = env->GetStringUTFChars(result, nullptr);
-        return utf8 ? strdup(utf8) : nullptr;
+        if(utf8)
+        {
+            retval = strdup(utf8);
+            env->ReleaseStringUTFChars(result, utf8);
+        }
     }
     return nullptr;
 }
@@ -3421,6 +3509,8 @@
                                                     "(Lorg/dbsoft/dwindows/DWTree;Lorg/dbsoft/dwindows/DWTreeItem;Ljava/lang/String;Landroid/graphics/drawable/Drawable;)V");
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, treeItemChange, handle, item, jstr, icon);
+        _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -3643,6 +3733,7 @@
                 env->CallVoidMethod(_dw_obj, containerNew, handle, jstr, (int)flags[z]);
                 if(_dw_jni_check_exception(env))
                     retval = DW_ERROR_GENERAL;
+                env->DeleteLocalRef(jstr);
             }
         }
     }
@@ -3761,6 +3852,7 @@
                 // Call the method on the object
                 env->CallVoidMethod(_dw_obj, containerChangeItem, handle, column, row, jstr);
                 _dw_jni_check_exception(env);
+                env->DeleteLocalRef(jstr);
             }
         }
         else if((columntype & DW_CFA_ULONG))
@@ -4046,6 +4138,7 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, containerChangeRowTitle, handle, row, jstr);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -4171,13 +4264,19 @@
             jclass clazz = _dw_find_class(env, DW_CLASS_NAME);
             // Get the method that you want to call
             jmethodID containerGetTitleStart = env->GetMethodID(clazz, "containerGetTitleStart",
-                                                               "(Landroid/widget/ListView;I)Ljava/lang/String;");
+                                                    "(Landroid/widget/ListView;I)Ljava/lang/String;");
             // Call the method on the object
             jstring jstr = (jstring)_dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, containerGetTitleStart, handle, (jint)flags), _DW_REFERENCE_NONE);
-            char *str;
-
-            if(jstr && (str = (char *)env->GetStringUTFChars(jstr, nullptr)))
-                retval = strdup(str);
+            if(jstr)
+            {
+                const char *str = env->GetStringUTFChars(jstr, nullptr);
+
+                if(str)
+                {
+                    retval = strdup(str);
+                    env->ReleaseStringUTFChars(jstr, str);
+                }
+            }
         }
     }
     return retval;
@@ -4218,10 +4317,16 @@
                                                                "(Landroid/widget/ListView;I)Ljava/lang/String;");
             // Call the method on the object
             jstring jstr = (jstring)_dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, containerGetTitleNext, handle, (jint)flags), _DW_REFERENCE_NONE);
-            char *str;
-
-            if(jstr && (str = (char *)env->GetStringUTFChars(jstr, nullptr)))
-                retval = strdup(str);
+            if(jstr)
+            {
+                const char *str = env->GetStringUTFChars(jstr, nullptr);
+
+                if(str)
+                {
+                    retval = strdup(str);
+                    env->ReleaseStringUTFChars(jstr, str);
+                }
+            }
         }
     }
     return retval;
@@ -4249,6 +4354,8 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, containerCursor, handle, jstr);
         _dw_jni_check_exception(env);
+        if(jstr)
+            env->DeleteLocalRef(jstr);
     }
 }
 
@@ -4297,6 +4404,8 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, containerRowDeleteByTitle, handle, jstr);
         _dw_jni_check_exception(env);
+        if(jstr)
+            env->DeleteLocalRef(jstr);
     }
 }
 
@@ -4379,9 +4488,11 @@
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, iconNew,
                                               file, bytearray, len, resid), _DW_REFERENCE_STRONG);
-        // Clean up after the array now that we are finished
+        // TODO: Clean up after the array now that we are finished
         //if(bytearray)
         //env->ReleaseByteArrayElements(bytearray, (jbyte *) data, 0);
+        if(file)
+            env->DeleteLocalRef(file);
         return result;
     }
     return nullptr;
@@ -4569,10 +4680,6 @@
 
     if((env = (JNIEnv *)pthread_getspecific(_dw_env_key)))
     {
-        // Construct a string
-        jstring file = nullptr;
-        if(filename)
-            file = env->NewStringUTF(filename);
         // Construct a byte array
         jbyteArray bytearray = nullptr;
         if(data && len > 0)
@@ -4582,6 +4689,10 @@
         }
         if(!_dw_jni_check_exception(env))
         {
+            // Construct a string
+            jstring file = nullptr;
+            if(filename)
+                file = env->NewStringUTF(filename);
             // First get the class that contains the method you need to call
             jclass clazz = _dw_find_class(env, DW_CLASS_NAME);
             // Get the method that you want to call
@@ -4594,9 +4705,11 @@
                                                                              file, bytearray, len,
                                                                              resid),
                                                   _DW_REFERENCE_STRONG);
-            // Clean up after the array now that we are finished
+            // TODO: Clean up after the array now that we are finished
             //if(bytearray)
             //env->ReleaseByteArrayElements(bytearray, (jbyte *) data, 0);
+            if(file)
+                env->DeleteLocalRef(file);
             return result;
         }
     }
@@ -4791,6 +4904,7 @@
                 env->DeleteGlobalRef(oldtypeface);
             pixmap->fontsize = atoi(fontname);
         }
+        env->DeleteLocalRef(jstr);
         return DW_ERROR_NONE;
     }
     return DW_ERROR_GENERAL;
@@ -5003,6 +5117,7 @@
 int API dw_html_raw(HWND handle, const char *string)
 {
     JNIEnv *env;
+    int retval = DW_ERROR_GENERAL;
 
     if(handle && (env = (JNIEnv *)pthread_getspecific(_dw_env_key)))
     {
@@ -5016,9 +5131,10 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, htmlRaw, handle, jstr);
         if(!_dw_jni_check_exception(env))
-            return DW_ERROR_NONE;
-    }
-    return DW_ERROR_GENERAL;
+            retval = DW_ERROR_NONE;
+        env->DeleteLocalRef(jstr);
+    }
+    return retval;
 }
 
 /*
@@ -5033,6 +5149,7 @@
 int API dw_html_url(HWND handle, const char *url)
 {
     JNIEnv *env;
+    int retval = DW_ERROR_GENERAL;
 
     if(handle && (env = (JNIEnv *)pthread_getspecific(_dw_env_key)))
     {
@@ -5046,9 +5163,10 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, htmlLoadURL, handle, jstr);
         if(!_dw_jni_check_exception(env))
-            return DW_ERROR_NONE;
-    }
-    return DW_ERROR_GENERAL;
+            retval = DW_ERROR_NONE;
+        env->DeleteLocalRef(jstr);
+    }
+    return retval;
 }
 
 /*
@@ -5064,6 +5182,7 @@
 int API dw_html_javascript_run(HWND handle, const char *script, void *scriptdata)
 {
     JNIEnv *env;
+    int retval = DW_ERROR_GENERAL;
 
     if(handle && (env = (JNIEnv *)pthread_getspecific(_dw_env_key)))
     {
@@ -5077,9 +5196,10 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, htmlJavascriptRun, handle, jstr, (jlong)scriptdata);
         if(!_dw_jni_check_exception(env))
-            return DW_ERROR_NONE;
-    }
-    return DW_ERROR_UNKNOWN;
+            retval = DW_ERROR_NONE;
+        env->DeleteLocalRef(jstr);
+    }
+    return retval;
 }
 
 /*
@@ -5310,6 +5430,7 @@
                                                     "(Lorg/dbsoft/dwindows/DWMenu;Ljava/lang/String;IIIILorg/dbsoft/dwindows/DWMenu;)Lorg/dbsoft/dwindows/DWMenuItem;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, menuAppendItem, menux, jstr, (int)itemid, (int)flags, end, check, submenux), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -5502,6 +5623,7 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, notebookPageSetText, handle, (jlong)pageid, jstr);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -5564,6 +5686,7 @@
                                                "(Ljava/lang/String;I)Landroid/widget/LinearLayout;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, windowNew, jstr, (int)flStyle), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(jstr);
         return result;
     }
     return nullptr;
@@ -5812,6 +5935,7 @@
 int API dw_window_set_font(HWND handle, const char *fontname)
 {
     JNIEnv *env;
+    int retval = DW_ERROR_GENERAL;
 
     if(handle && (env = (JNIEnv *)pthread_getspecific(_dw_env_key)))
     {
@@ -5825,9 +5949,10 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, windowSetFont, handle, jstr);
         if(!_dw_jni_check_exception(env))
-            return DW_ERROR_NONE;
-    }
-    return DW_ERROR_GENERAL;
+            retval = DW_ERROR_NONE;
+        env->DeleteLocalRef(jstr);
+    }
+    return retval;
 }
 
 /*
@@ -5853,7 +5978,15 @@
         jstring jstr = (jstring)_dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, windowGetFont, handle), _DW_REFERENCE_NONE);
 
         if(jstr)
-            fontname = strdup(env->GetStringUTFChars(jstr, nullptr));
+        {
+            const char *str = env->GetStringUTFChars(jstr, nullptr);
+
+            if(str)
+            {
+                fontname = strdup(str);
+                env->ReleaseStringUTFChars(jstr, str);
+            }
+        }
     }
     return fontname;
 }
@@ -5919,6 +6052,7 @@
     if((env = (JNIEnv *)pthread_getspecific(_dw_env_key)))
     {
         const char *utf8 = nullptr;
+        char *retval = nullptr;
 
         // First get the class that contains the method you need to call
         jclass clazz = _dw_find_class(env, DW_CLASS_NAME);
@@ -5930,7 +6064,11 @@
         // Get the UTF8 string result
         if(result)
             utf8 = env->GetStringUTFChars(result, nullptr);
-        return utf8 ? strdup(utf8) : nullptr;
+        if(utf8)
+        {
+            retval = strdup(utf8);
+            env->ReleaseStringUTFChars(result, utf8);
+        }
     }
     return nullptr;
 }
@@ -5957,6 +6095,7 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, windowSetText, handle, jstr);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -6075,6 +6214,8 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, windowSetBitmapFromData, handle, (int)resid, jstr);
         _dw_jni_check_exception(env);
+        if(jstr)
+            env->DeleteLocalRef(jstr);
     }
 }
 
@@ -6345,6 +6486,7 @@
     {
         JNIEnv *jenv;
         const char *release = nullptr;
+        jstring jstr = nullptr;
 
         if((jenv = (JNIEnv *)pthread_getspecific(_dw_env_key)))
         {
@@ -6354,13 +6496,15 @@
             jmethodID androidGetRelease = jenv->GetMethodID(clazz, "androidGetRelease",
                                                            "()Ljava/lang/String;");
             // Call the method on the object
-            jstring jstr = (jstring)_dw_jni_check_result(jenv, jenv->CallObjectMethod(_dw_obj, androidGetRelease), _DW_REFERENCE_NONE);
+            jstr = (jstring)_dw_jni_check_result(jenv, jenv->CallObjectMethod(_dw_obj, androidGetRelease), _DW_REFERENCE_NONE);
 
             if(jstr)
                 release = jenv->GetStringUTFChars(jstr, nullptr);
         }
         snprintf(osName, _DW_ENV_STRING_SIZE-1, "Android%s%s",
                  release ? " " : "", release ? release : "");
+        if(release)
+            jenv->ReleaseStringUTFChars(jstr, release);
     }
     memset(env, '\0', sizeof(DWEnv));
 
@@ -6430,6 +6574,7 @@
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, windowSetData, window, jstr, (jlong)data);
         _dw_jni_check_exception(env);
+        env->DeleteLocalRef(jstr);
     }
 }
 
@@ -6459,6 +6604,7 @@
         retval = (void *)env->CallLongMethod(_dw_obj, windowGetData, window, jstr);
         if(_dw_jni_check_exception(env))
             retval = nullptr;
+        env->DeleteLocalRef(jstr);
     }
     return retval;
 }
@@ -7571,6 +7717,8 @@
         _dw_android_api = env->CallIntMethod(_dw_obj, dwInit, appid, appname);
         if(_dw_jni_check_exception(env))
             _dw_android_api = 0;
+        env->DeleteLocalRef(appid);
+        env->DeleteLocalRef(appname);
     }
     return DW_ERROR_NONE;
 }
@@ -7674,6 +7822,7 @@
         retval = env->CallIntMethod(_dw_obj, browseURL, jstr);
         if(_dw_jni_check_exception(env))
             retval = DW_ERROR_UNKNOWN;
+        env->DeleteLocalRef(jstr);
     }
     return retval;
 }
@@ -7735,6 +7884,7 @@
         print->printjob = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, printRun, (jlong)pr, (jint)print->flags, jstr, (jint)print->pages, (jint)flags), _DW_REFERENCE_WEAK);
         if(print->printjob)
             retval = DW_ERROR_NONE;
+        env->DeleteLocalRef(jstr);
     }
     return retval;
 }
@@ -7815,6 +7965,8 @@
                                                      "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroidx/core/app/NotificationCompat$Builder;");
         // Call the method on the object
         jobject result = _dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, notificationNew, ntitle, image, ndesc, appid), _DW_REFERENCE_WEAK);
+        env->DeleteLocalRef(appid);
+        env->DeleteLocalRef(ntitle);
         return result;
     }
     return nullptr;