From b58c554cdee28b1c79ad8a097da63751a237faee Mon Sep 17 00:00:00 2001 From: Joachim Kuebart Date: Fri, 13 Aug 2021 17:26:57 +0200 Subject: [PATCH 1/3] Java: Option to detach from the JVM in the thread destructor. --- CHANGES.current | 10 ++++++++++ Lib/java/director.swg | 28 +++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/CHANGES.current b/CHANGES.current index 4a3bd7d98..9d804f53b 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,16 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.1.0 (in progress) =========================== +2022-05-23: jkuebart + [Java] On some versions of Android, specifically Android 6, + detaching the current thread from the JVM after every invocation + causes a memory leak. + + Offer SWIG_JAVA_DETACH_ON_THREAD_END to configure a behaviour + where the JVM is only detached in the thread destructor. + + See https://developer.android.com/training/articles/perf-jni#threads. + 2022-05-15: erezgeva, eiselekd [Lua, Perl, Octave, PHP, Tcl] #2275 #2276 #2283 Add argcargv.i library containing (int ARGC, char **ARGV) multi-argument typemaps. diff --git a/Lib/java/director.swg b/Lib/java/director.swg index e911a3da7..f7c4f2d8a 100644 --- a/Lib/java/director.swg +++ b/Lib/java/director.swg @@ -51,6 +51,22 @@ SWIGINTERN int Swig::GetThreadName(char *name, size_t len) { #endif +#if defined(SWIG_JAVA_DETACH_ON_THREAD_END) +#include +namespace { + + void detach(void* jvm) { + static_cast(jvm)->DetachCurrentThread(); + } + + pthread_key_t detachKey; + void makeDetachKey() { + pthread_key_create(&detachKey, detach); + } + +} +#endif + namespace Swig { /* Java object wrapper */ @@ -201,9 +217,19 @@ namespace Swig { #else director_->swig_jvm_->AttachCurrentThread(jenv, &args); #endif + +#if defined(SWIG_JAVA_DETACH_ON_THREAD_END) + // At least on Android 6, detaching after every call causes a memory leak. + // Instead, register a thread desructor and detach only when the thread ends. + // See https://developer.android.com/training/articles/perf-jni#threads + static pthread_once_t once = PTHREAD_ONCE_INIT; + + pthread_once(&once, makeDetachKey); + pthread_setspecific(detachKey, director->swig_jvm_); +#endif } ~JNIEnvWrapper() { -#if !defined(SWIG_JAVA_NO_DETACH_CURRENT_THREAD) +#if !defined(SWIG_JAVA_DETACH_ON_THREAD_END) && !defined(SWIG_JAVA_NO_DETACH_CURRENT_THREAD) // Some JVMs, eg jdk-1.4.2 and lower on Solaris have a bug and crash with the DetachCurrentThread call. // However, without this call, the JVM hangs on exit when the thread was not created by the JVM and creates a memory leak. if (env_status == JNI_EDETACHED) From 36dc81808bc5c5e98ad78142b60a4268125a378e Mon Sep 17 00:00:00 2001 From: Joachim Kuebart Date: Tue, 24 May 2022 14:34:09 +0200 Subject: [PATCH 2/3] Java: Move auxiliary methods into JObjectWrapper. --- Lib/java/director.swg | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/Lib/java/director.swg b/Lib/java/director.swg index f7c4f2d8a..70f4c561c 100644 --- a/Lib/java/director.swg +++ b/Lib/java/director.swg @@ -53,18 +53,6 @@ SWIGINTERN int Swig::GetThreadName(char *name, size_t len) { #if defined(SWIG_JAVA_DETACH_ON_THREAD_END) #include -namespace { - - void detach(void* jvm) { - static_cast(jvm)->DetachCurrentThread(); - } - - pthread_key_t detachKey; - void makeDetachKey() { - pthread_key_create(&detachKey, detach); - } - -} #endif namespace Swig { @@ -149,6 +137,19 @@ namespace Swig { } } +#if defined(SWIG_JAVA_DETACH_ON_THREAD_END) + static void detach(void* jvm) { + static_cast(jvm)->DetachCurrentThread(); + } + + static void makeDetachKey() { + pthread_key_create(&detach_key_, detach); + } + + /* thread-local key to register a destructor */ + static pthread_key_t detach_key_; +#endif + private: /* pointer to Java object */ jobject jthis_; @@ -156,6 +157,10 @@ namespace Swig { bool weak_global_; }; +#if defined(SWIG_JAVA_DETACH_ON_THREAD_END) + pthread_key_t JObjectWrapper::detach_key_; +#endif + /* Local JNI reference deleter */ class LocalRefGuard { JNIEnv *jenv_; @@ -224,8 +229,8 @@ namespace Swig { // See https://developer.android.com/training/articles/perf-jni#threads static pthread_once_t once = PTHREAD_ONCE_INIT; - pthread_once(&once, makeDetachKey); - pthread_setspecific(detachKey, director->swig_jvm_); + pthread_once(&once, JObjectWrapper::makeDetachKey); + pthread_setspecific(JObjectWrapper::detach_key_, director->swig_jvm_); #endif } ~JNIEnvWrapper() { From aef97a57833410ca521360437b78d315029bfabc Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Sat, 28 May 2022 10:22:34 +0100 Subject: [PATCH 3/3] Cosmetic tidyup in SWIG_JAVA_DETACH_ON_THREAD_END code --- Lib/java/director.swg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/java/director.swg b/Lib/java/director.swg index 70f4c561c..536513557 100644 --- a/Lib/java/director.swg +++ b/Lib/java/director.swg @@ -138,11 +138,11 @@ namespace Swig { } #if defined(SWIG_JAVA_DETACH_ON_THREAD_END) - static void detach(void* jvm) { - static_cast(jvm)->DetachCurrentThread(); + static void detach(void *jvm) { + static_cast(jvm)->DetachCurrentThread(); } - static void makeDetachKey() { + static void make_detach_key() { pthread_key_create(&detach_key_, detach); } @@ -229,7 +229,7 @@ namespace Swig { // See https://developer.android.com/training/articles/perf-jni#threads static pthread_once_t once = PTHREAD_ONCE_INIT; - pthread_once(&once, JObjectWrapper::makeDetachKey); + pthread_once(&once, JObjectWrapper::make_detach_key); pthread_setspecific(JObjectWrapper::detach_key_, director->swig_jvm_); #endif }