: path_(path),
handle_(handle),
needs_native_bridge_(false),
- class_loader_(env->NewGlobalRef(class_loader)),
+ class_loader_(env->NewWeakGlobalRef(class_loader)),
jni_on_load_lock_("JNI_OnLoad lock"),
jni_on_load_cond_("JNI_OnLoad condition variable", jni_on_load_lock_),
jni_on_load_thread_id_(self->GetThreadId()),
~SharedLibrary() {
Thread* self = Thread::Current();
if (self != nullptr) {
- self->GetJniEnv()->DeleteGlobalRef(class_loader_);
+ self->GetJniEnv()->DeleteWeakGlobalRef(class_loader_);
}
}
- jobject GetClassLoader() const {
+ jweak GetClassLoader() const {
return class_loader_;
}
return needs_native_bridge_;
}
- void* FindSymbol(const std::string& symbol_name) {
+ void* FindSymbol(const std::string& symbol_name, const char* shorty = nullptr) {
+ return NeedsNativeBridge()
+ ? FindSymbolWithNativeBridge(symbol_name.c_str(), shorty)
+ : FindSymbolWithoutNativeBridge(symbol_name.c_str());
+ }
+
+ void* FindSymbolWithoutNativeBridge(const std::string& symbol_name) {
CHECK(!NeedsNativeBridge());
return dlsym(handle_, symbol_name.c_str());
// True if a native bridge is required.
bool needs_native_bridge_;
- // The ClassLoader this library is associated with, a global JNI reference that is
+ // The ClassLoader this library is associated with, a weak global JNI reference that is
// created/deleted with the scope of the library.
- const jobject class_loader_;
+ const jweak class_loader_;
// Guards remaining items.
Mutex jni_on_load_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
STLDeleteValues(&libraries_);
}
- void Dump(std::ostream& os) const {
+ // NO_THREAD_SAFETY_ANALYSIS since this may be called from Dumpable. Dumpable can't be annotated
+ // properly due to the template. The caller should be holding the jni_libraries_lock_.
+ void Dump(std::ostream& os) const NO_THREAD_SAFETY_ANALYSIS {
+ Locks::jni_libraries_lock_->AssertHeld(Thread::Current());
bool first = true;
for (const auto& library : libraries_) {
if (!first) {
}
}
- size_t size() const {
+ size_t size() const REQUIRES(Locks::jni_libraries_lock_) {
return libraries_.size();
}
- SharedLibrary* Get(const std::string& path) {
+ SharedLibrary* Get(const std::string& path) REQUIRES(Locks::jni_libraries_lock_) {
auto it = libraries_.find(path);
return (it == libraries_.end()) ? nullptr : it->second;
}
- void Put(const std::string& path, SharedLibrary* library) {
+ void Put(const std::string& path, SharedLibrary* library)
+ REQUIRES(Locks::jni_libraries_lock_) {
libraries_.Put(path, library);
}
const mirror::ClassLoader* declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
ScopedObjectAccessUnchecked soa(Thread::Current());
for (const auto& lib : libraries_) {
- SharedLibrary* library = lib.second;
+ SharedLibrary* const library = lib.second;
if (soa.Decode<mirror::ClassLoader*>(library->GetClassLoader()) != declaring_class_loader) {
// We only search libraries loaded by the appropriate ClassLoader.
continue;
}
// Try the short name then the long name...
- void* fn;
- if (library->NeedsNativeBridge()) {
- const char* shorty = m->GetShorty();
- fn = library->FindSymbolWithNativeBridge(jni_short_name, shorty);
- if (fn == nullptr) {
- fn = library->FindSymbolWithNativeBridge(jni_long_name, shorty);
- }
- } else {
- fn = library->FindSymbol(jni_short_name);
- if (fn == nullptr) {
- fn = library->FindSymbol(jni_long_name);
- }
+ const char* shorty = library->NeedsNativeBridge()
+ ? m->GetShorty()
+ : nullptr;
+ void* fn = library->FindSymbol(jni_short_name, shorty);
+ if (fn == nullptr) {
+ fn = library->FindSymbol(jni_long_name, shorty);
}
if (fn != nullptr) {
VLOG(jni) << "[Found native code for " << PrettyMethod(m)
return nullptr;
}
+ // Unload native libraries with cleared class loaders.
+ void UnloadNativeLibraries()
+ REQUIRES(!Locks::jni_libraries_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ typedef void (*JNI_OnUnloadFn)(JavaVM*, void*);
+ std::vector<JNI_OnUnloadFn> unload_functions;
+ {
+ MutexLock mu(soa.Self(), *Locks::jni_libraries_lock_);
+ for (auto it = libraries_.begin(); it != libraries_.end(); ) {
+ SharedLibrary* const library = it->second;
+ // If class loader is null then it was unloaded, call JNI_OnUnload.
+ if (soa.Decode<mirror::ClassLoader*>(library->GetClassLoader()) == nullptr) {
+ void* const sym = library->FindSymbol("JNI_OnUnload", nullptr);
+ if (sym == nullptr) {
+ VLOG(jni) << "[No JNI_OnUnload found in \"" << library->GetPath() << "\"]";
+ } else {
+ VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]";
+ JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym);
+ unload_functions.push_back(jni_on_unload);
+ }
+ delete library;
+ it = libraries_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ // Do this without holding the jni libraries lock to prevent possible deadlocks.
+ for (JNI_OnUnloadFn fn : unload_functions) {
+ VLOG(jni) << "Calling JNI_OnUnload";
+ (*fn)(soa.Vm(), nullptr);
+ }
+ }
+
private:
- AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibraries> libraries_;
+ AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibraries> libraries_
+ GUARDED_BY(Locks::jni_libraries_lock_);
};
-
class JII {
public:
static jint DestroyJavaVM(JavaVM* vm) {
}
}
+void JavaVMExt::UnloadNativeLibraries() {
+ libraries_.get()->UnloadNativeLibraries();
+}
+
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader,
std::string* error_msg) {
error_msg->clear();
void* sym;
if (needs_native_bridge) {
library->SetNeedsNativeBridge();
- sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr);
- } else {
- sym = dlsym(handle, "JNI_OnLoad");
}
+ sym = library->FindSymbol("JNI_OnLoad", nullptr);
if (sym == nullptr) {
VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
was_successful = true;
public class Main {
static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/141-class-unload-ex.jar";
+ static String nativeLibraryName;
public static void main(String[] args) throws Exception {
+ nativeLibraryName = args[0];
Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
if (pathClassLoader == null) {
throw new AssertionError("Couldn't find path class loader class");
testNoUnloadInvoke(constructor);
// Test that we don't unload if we have an instance.
testNoUnloadInstance(constructor);
+ // Test JNI_OnLoad and JNI_OnUnload.
+ testLoadAndUnloadLibrary(constructor);
// Stress test to make sure we dont leak memory.
stressTest(constructor);
} catch (Exception e) {
System.out.println(loader.get());
}
+ private static void testLoadAndUnloadLibrary(Constructor constructor) throws Exception {
+ WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor);
+ // No strong refernces to class loader, should get unloaded.
+ Runtime.getRuntime().gc();
+ // If the weak reference is cleared, then it was unloaded.
+ System.out.println(loader.get());
+ }
+
private static void testNoUnloadInvoke(Constructor constructor) throws Exception {
WeakReference<ClassLoader> loader =
new WeakReference((ClassLoader) constructor.newInstance(
return new WeakReference(loader);
}
+ private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor constructor)
+ throws Exception {
+ ClassLoader loader = (ClassLoader) constructor.newInstance(
+ DEX_FILE, ClassLoader.getSystemClassLoader());
+ Class intHolder = loader.loadClass("IntHolder");
+ Method setValue = intHolder.getDeclaredMethod("loadLibrary", String.class);
+ setValue.invoke(intHolder, nativeLibraryName);
+ return new WeakReference(loader);
+ }
}