Skip to content

Commit 1d6f4b4

Browse files
committed
Merge branch 'java-director-leak'
* java-director-leak: Cosmetic tidyup in SWIG_JAVA_DETACH_ON_THREAD_END code Java: Move auxiliary methods into JObjectWrapper. Java: Option to detach from the JVM in the thread destructor. Conflicts: CHANGES.current
2 parents 8e3f0fd + aef97a5 commit 1d6f4b4

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

CHANGES.current

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
77
Version 4.1.0 (in progress)
88
===========================
99

10+
2022-05-28: jkuebart
11+
[Java] On some versions of Android, specifically Android 6,
12+
detaching the current thread from the JVM after every invocation
13+
causes a memory leak.
14+
15+
Offer SWIG_JAVA_DETACH_ON_THREAD_END to configure a behaviour
16+
where the JVM is only detached in the thread destructor.
17+
18+
See https://developer.android.com/training/articles/perf-jni#threads.
19+
1020
2022-05-27: xypron
1121
[Python] #2277 Define PY_SSIZE_T_CLEAN macro before #include "Python.h" as
1222
recommended in Python 3.7 and later.

Lib/java/director.swg

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ SWIGINTERN int Swig::GetThreadName(char *name, size_t len) {
5151

5252
#endif
5353

54+
#if defined(SWIG_JAVA_DETACH_ON_THREAD_END)
55+
#include <pthread.h>
56+
#endif
57+
5458
namespace Swig {
5559

5660
/* Java object wrapper */
@@ -133,13 +137,30 @@ namespace Swig {
133137
}
134138
}
135139

140+
#if defined(SWIG_JAVA_DETACH_ON_THREAD_END)
141+
static void detach(void *jvm) {
142+
static_cast<JavaVM *>(jvm)->DetachCurrentThread();
143+
}
144+
145+
static void make_detach_key() {
146+
pthread_key_create(&detach_key_, detach);
147+
}
148+
149+
/* thread-local key to register a destructor */
150+
static pthread_key_t detach_key_;
151+
#endif
152+
136153
private:
137154
/* pointer to Java object */
138155
jobject jthis_;
139156
/* Local or global reference flag */
140157
bool weak_global_;
141158
};
142159

160+
#if defined(SWIG_JAVA_DETACH_ON_THREAD_END)
161+
pthread_key_t JObjectWrapper::detach_key_;
162+
#endif
163+
143164
/* Local JNI reference deleter */
144165
class LocalRefGuard {
145166
JNIEnv *jenv_;
@@ -201,9 +222,19 @@ namespace Swig {
201222
#else
202223
director_->swig_jvm_->AttachCurrentThread(jenv, &args);
203224
#endif
225+
226+
#if defined(SWIG_JAVA_DETACH_ON_THREAD_END)
227+
// At least on Android 6, detaching after every call causes a memory leak.
228+
// Instead, register a thread desructor and detach only when the thread ends.
229+
// See https://developer.android.com/training/articles/perf-jni#threads
230+
static pthread_once_t once = PTHREAD_ONCE_INIT;
231+
232+
pthread_once(&once, JObjectWrapper::make_detach_key);
233+
pthread_setspecific(JObjectWrapper::detach_key_, director->swig_jvm_);
234+
#endif
204235
}
205236
~JNIEnvWrapper() {
206-
#if !defined(SWIG_JAVA_NO_DETACH_CURRENT_THREAD)
237+
#if !defined(SWIG_JAVA_DETACH_ON_THREAD_END) && !defined(SWIG_JAVA_NO_DETACH_CURRENT_THREAD)
207238
// Some JVMs, eg jdk-1.4.2 and lower on Solaris have a bug and crash with the DetachCurrentThread call.
208239
// However, without this call, the JVM hangs on exit when the thread was not created by the JVM and creates a memory leak.
209240
if (env_status == JNI_EDETACHED)

0 commit comments

Comments
 (0)