I just finished writing a Java wrapper for a complex C++ library, and had a fair bit of trouble finding solutions to (or even basic documentation pertaining to) some of the problems that came up.
Things seem pretty easy if you just need to call some C++ functions from Java: you use JNI and do a little gruntwork to translate data to and from Java objects, or let SWIG do the heavy lifting for you. However, in my case things were harder, because the library is multithreaded (using boost threads) and makes heavy use of callbacks into the client (i.e., Java).
The main complication is that each native thread that calls a Java method must do so through its own JNIEnv pointer, obtained by calling AttachCurrentThread on a JavaVM instance. Each such native thread (which may be created and managed by code you can’t change) must eventually be detached from the JVM by calling DetachCurrentThread within it, or it seems memory leaks and JVM shutdown issues can result. There is no obvious way to detach the thread from the outside, so if you let the thread die or get away before it calls DetachCurrentThread you’re stuck.
Since a C++ wrapper for the Java callbacks is needed anyway, one easy solution is to attach before each callback and detach afterwards. However, this may be very expensive; I haven’t measured the cost, but one post I found quoted a factor of 6 slowdown. My alternative solution uses boost::thread_specific_ptr to detach each native thread just before it dies:
#include <boost/thread/tss.hpp> static JavaVM *vm = NULL; class ThreadJNIEnv { public: bool _detach; JNIEnv *env; ThreadJNIEnv() { cout << "Attaching " << boost::this_thread::get_id() << endl; vm->AttachCurrentThread((void **) &env, NULL); ASSERT(env != NULL); _detach = true; } ThreadJNIEnv(JNIEnv *e) { env = e; _detach = false; } ~ThreadJNIEnv() { if (_detach) { cout << "Detaching " << boost::this_thread::get_id() << endl; vm->DetachCurrentThread(); } } }; static boost::thread_specific_ptr<ThreadJNIEnv> envs; JNIEnv *getJNIEnv(){ ThreadJNIEnv *tenv = envs.get(); if (tenv == NULL) { tenv = new ThreadJNIEnv(); envs.reset(tenv); } return tenv->env; } bool init(JNIEnv *env) { if (!env->GetJavaVM(&vm) < 0) return false; envs.reset(new ThreadJNIEnv(env)); return true; } |
If your main function is in Java, you initialize the library by calling init(env) with the current thread’s JNIEnv (with multiple Java threads, you may have to take care). If it’s C++, you just set “vm” directly when you create the JVM instance.
Then, any function not in a direct JNI call just uses getJNIEnv() to get a JNIEnv pointer that’s valid for the current thread. This will work correctly both for indirect calls from Java, and from native threads, which will be automatically attached on the first call to getJNIEnv and automatically detached before death.
Seemed to work for me on Ubuntu; YMMV.
Oty
Hi,
I had the same library. But after N call to java from C++ thread.
application crashed!
have you a idea.
Timothy Wall
You should only detach if not already attached (pseudo-code follows):
attached = (*jvm)->GetEnv(…)
if (!attached) (*jvm)->AttachCrrentThread(…)
// do java callback
if (!attached) (*jvm)->DetachCurrentThread(jvm)
Java thread leaks when calling back from native thread via JNI | SuperBlog
[…] is an optimization described in several places (like here and here) that recommends using mechanisms based on thread local storage to eliminate this problem: […]
Malcolm Wilkins
Warning: this technique does not currently work on 64-bit Linux platforms (confirmed Java bug: https://bugs.openjdk.java.net/browse/JDK-8033696)
Malcolm Wilkins
This will be fixed in Java 7 update 80. See my SO post about this issue here: http://stackoverflow.com/questions/20325792/java-thread-leaks-when-calling-back-from-native-thread-via-jni
Java thread leaks when calling back from native thread via JNI | Questions
[…] is an optimization described in several places (like here and here) that recommends using mechanisms based on thread local storage to eliminate this problem: […]
Android JNI - Call AttachCurrentThread without DetachCurrentThread - Tutorial Guruji
[…] Not calling DetachCurrentThread() will definitely cause a memory leak; other consequences are JVM-specific, and probably irrelevant for Android apps, where the JVM shuts down when the process exits. There are quite a few C++ wrappers that help to manage thread Attach/Detach, see for example: http://w01fe.com/blog/2009/05/c-callbacks-into-java-via-jni-made-easyier […]
Android JNI – Call AttachCurrentThread without DetachCurrentThread - Sarkari Job Alert
[…] Not calling DetachCurrentThread() will definitely cause a memory leak; other consequences are JVM-specific, and probably irrelevant for Android apps, where the JVM shuts down when the process exits. There are quite a few C++ wrappers that help to manage thread Attach/Detach, see for example: http://w01fe.com/blog/2009/05/c-callbacks-into-java-via-jni-made-easyier […]
Android JNI - Call AttachCurrentThread without DetachCurrentThread
[…] Not calling DetachCurrentThread() will definitely cause a memory leak; other consequences are JVM-specific, and probably irrelevant for Android apps, where the JVM shuts down when the process exits. There are quite a few C++ wrappers that help to manage thread Attach/Detach, see for example: http://w01fe.com/blog/2009/05/c-callbacks-into-java-via-jni-made-easyier […]
[FIXED] Android JNI - Call AttachCurrentThread without DetachCurrentThread - FixeMe
[…] Not calling DetachCurrentThread() will definitely cause a memory leak; other consequences are JVM-specific, and probably irrelevant for Android apps, where the JVM shuts down when the process exits. There are quite a few C++ wrappers that help to manage thread Attach/Detach, see for example: http://w01fe.com/blog/2009/05/c-callbacks-into-java-via-jni-made-easyier […]
Android JNI – Call AttachCurrentThread without DetachCurrentThread - Code Solution
[…] Not calling DetachCurrentThread() will definitely cause a memory leak; other consequences are JVM-specific, and probably irrelevant for Android apps, where the JVM shuts down when the process exits. There are quite a few C++ wrappers that help to manage thread Attach/Detach, see for example: http://w01fe.com/blog/2009/05/c-callbacks-into-java-via-jni-made-easyier […]