publicstaticvoid main(String[] args) throws Exception {
nativeLibraryName = args[0]; Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); if (pathClassLoader == null) { thrownew AssertionError("Couldn't find path class loader class");
}
Constructor<?> constructor =
pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class); try {
testUnloadClass(constructor);
testUnloadLoader(constructor); // Test that we don't unload if we have an instance.
testNoUnloadInstance(constructor); // Test JNI_OnLoad and JNI_OnUnload.
testLoadAndUnloadLibrary(constructor); // Test that stack traces keep the classes live.
testStackTrace(constructor); // Stress test to make sure we dont leak memory.
stressTest(constructor); // Test that the oat files are unloaded.
testOatFilesUnloaded(getPid()); // Test that objects keep class loader live for sticky GC.
testStickyUnload(constructor); // Test that copied methods recorded in a stack trace prevents unloading.
testCopiedMethodInStackTrace(constructor); // Test that code preventing unloading holder classes of copied methods recorded in // a stack trace does not crash when processing a copied method in the boot class path.
testCopiedBcpMethodInStackTrace(); // Test that code preventing unloading holder classes of copied methods recorded in // a stack trace does not crash when processing a copied method in an app image.
testCopiedAppImageMethodInStackTrace(); // Test that the runtime uses the right allocator when creating conflict methods.
testConflictMethod(constructor);
testConflictMethod2(constructor);
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
privatestaticvoid testOatFilesUnloaded(int pid) throws Exception {
System.loadLibrary(nativeLibraryName); // Stop the JIT to ensure its threads and work queue are not keeping classes // artifically alive.
stopJit();
doUnloading();
System.runFinalization();
BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps"));
String line; int count = 0; while ((line = reader.readLine()) != null) { if (line.contains("141-class-unload-ex.odex") ||
line.contains("141-class-unload-ex.vdex")) {
System.out.println(line);
++count;
}
}
System.out.println("Number of loaded unload-ex maps " + count);
startJit();
}
privatestaticvoid stressTest(Constructor<?> constructor) throws Exception { for (int i = 0; i <= 100; ++i) {
setUpUnloadLoader(constructor, false); if (i % 10 == 0) {
Runtime.getRuntime().gc();
}
}
}
privatestaticvoid doUnloading() { // Do multiple GCs to prevent rare flakiness if some other thread is keeping the // classloader live. for (int i = 0; i < 5; ++i) {
Runtime.getRuntime().gc();
}
}
privatestaticvoid testUnloadClass(Constructor<?> constructor) throws Exception {
WeakReference<Class> klass = setUpUnloadClassWeak(constructor); // No strong references to class loader, should get unloaded.
doUnloading();
WeakReference<Class> klass2 = setUpUnloadClassWeak(constructor);
doUnloading(); // If the weak reference is cleared, then it was unloaded.
System.out.println(klass.get());
System.out.println(klass2.get());
}
privatestaticvoid testUnloadLoader(Constructor<?> constructor) throws Exception {
WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true); // No strong references to class loader, should get unloaded.
doUnloading(); // If the weak reference is cleared, then it was unloaded.
System.out.println(loader.get());
}
privatestaticvoid testLoadAndUnloadLibrary(Constructor<?> constructor) throws Exception {
WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor); // No strong references to class loader, should get unloaded.
doUnloading(); // If the weak reference is cleared, then it was unloaded.
System.out.println(loader.get());
}
// Regression test for public issue 227182. privatestaticvoid testStickyUnload(Constructor<?> constructor) throws Exception {
String s = ""; for (int i = 0; i < 10; ++i) {
s = ""; // The object is the only thing preventing the class loader from being unloaded.
Object o = allocObjectInOtherClassLoader(constructor); for (int j = 0; j < 1000; ++j) {
s += j + " ";
} // Make sure the object still has a valid class (hasn't been incorrectly unloaded).
s += o.getClass().getName();
o = null;
}
System.out.println("Too small " + (s.length() < 1000));
}
privatestaticvoid assertStackTraceContains(Throwable t, String className, String methodName) { boolean found = false; for (StackTraceElement e : t.getStackTrace()) { if (className.equals(e.getClassName()) && methodName.equals(e.getMethodName())) {
found = true; break;
}
} if (!found) { thrownew Error("Did not find " + className + "." + methodName);
}
}
privatestaticvoid testConflictMethod(Constructor<?> constructor) throws Exception { // Load and unload a few class loaders to force re-use of the native memory where we // used to allocate the conflict table. for (int i = 0; i < 2; i++) {
$noinline$invokeConflictMethod(constructor);
doUnloading();
} Class<?> impl = Class.forName("ConflictSuper");
ConflictIface iface = (ConflictIface) impl.newInstance();
$noinline$callAllMethods(iface);
}
privatestaticvoid $noinline$invokeConflictMethod2(Constructor<?> constructor) throws Exception { // We need three class loaders to expose the issue: the main one with the top super class, // then a second one with the abstract class which we used to wrongly return as an IMT // owner, and the concrete class in a different class loader. Class<?> cls = Class.forName("dalvik.system.InMemoryDexClassLoader");
Constructor<?> inMemoryConstructor =
cls.getDeclaredConstructor(ByteBuffer.class, ClassLoader.class);
ClassLoader inMemoryLoader = (ClassLoader) inMemoryConstructor.newInstance(
ByteBuffer.wrap(DEX_BYTES), ClassLoader.getSystemClassLoader());
ClassLoader loader = (ClassLoader) constructor.newInstance(
DEX_FILE, LIBRARY_SEARCH_PATH, inMemoryLoader); Class<?> impl = loader.loadClass("ConflictImpl2");
ConflictIface iface = (ConflictIface) impl.newInstance();
$noinline$callAllMethods(iface);
}
privatestaticvoid testConflictMethod2(Constructor<?> constructor) throws Exception { // Load and unload a few class loaders to force re-use of the native memory where we // used to allocate the conflict table. for (int i = 0; i < 2; i++) {
$noinline$invokeConflictMethod2(constructor);
doUnloading();
} Class<?> impl = Class.forName("ConflictSuper");
ConflictIface iface = (ConflictIface) impl.newInstance();
$noinline$callAllMethods(iface);
}
privatestaticvoid waitForCompilation(Class<?> intHolder) throws Exception { // Load the native library so that we can call waitForCompilation.
Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
loadLibrary.invoke(intHolder, nativeLibraryName); // Wait for JIT compilation to finish since the async threads may prevent unloading.
Method waitForCompilation = intHolder.getDeclaredMethod("waitForCompilation");
waitForCompilation.invoke(intHolder);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.