Main.java revision 45a8522898702f6a725ae19d97bceedc8fc609a6
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import java.lang.ref.WeakReference; 18import java.lang.reflect.Constructor; 19import java.lang.reflect.Method; 20 21public class Main { 22 static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/141-class-unload-ex.jar"; 23 static String nativeLibraryName; 24 25 public static void main(String[] args) throws Exception { 26 nativeLibraryName = args[0]; 27 Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); 28 if (pathClassLoader == null) { 29 throw new AssertionError("Couldn't find path class loader class"); 30 } 31 Constructor constructor = 32 pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class); 33 try { 34 testUnloadClass(constructor); 35 testUnloadLoader(constructor); 36 // Test that we don't unload if we have a Method keeping the class live. 37 testNoUnloadInvoke(constructor); 38 // Test that we don't unload if we have an instance. 39 testNoUnloadInstance(constructor); 40 // Test JNI_OnLoad and JNI_OnUnload. 41 testLoadAndUnloadLibrary(constructor); 42 // Stress test to make sure we dont leak memory. 43 stressTest(constructor); 44 } catch (Exception e) { 45 System.out.println(e); 46 } 47 } 48 49 private static void stressTest(Constructor constructor) throws Exception { 50 for (int i = 0; i <= 100; ++i) { 51 setUpUnloadLoader(constructor, false); 52 if (i % 10 == 0) { 53 Runtime.getRuntime().gc(); 54 } 55 } 56 } 57 58 private static void testUnloadClass(Constructor constructor) throws Exception { 59 WeakReference<Class> klass = setUpUnloadClass(constructor); 60 // No strong refernces to class loader, should get unloaded. 61 Runtime.getRuntime().gc(); 62 WeakReference<Class> klass2 = setUpUnloadClass(constructor); 63 Runtime.getRuntime().gc(); 64 // If the weak reference is cleared, then it was unloaded. 65 System.out.println(klass.get()); 66 System.out.println(klass2.get()); 67 } 68 69 private static void testUnloadLoader(Constructor constructor) 70 throws Exception { 71 WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true); 72 // No strong refernces to class loader, should get unloaded. 73 Runtime.getRuntime().gc(); 74 // If the weak reference is cleared, then it was unloaded. 75 System.out.println(loader.get()); 76 } 77 78 private static void testLoadAndUnloadLibrary(Constructor constructor) throws Exception { 79 WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor); 80 // No strong refernces to class loader, should get unloaded. 81 Runtime.getRuntime().gc(); 82 // If the weak reference is cleared, then it was unloaded. 83 System.out.println(loader.get()); 84 } 85 86 private static void testNoUnloadInvoke(Constructor constructor) throws Exception { 87 WeakReference<ClassLoader> loader = 88 new WeakReference((ClassLoader) constructor.newInstance( 89 DEX_FILE, ClassLoader.getSystemClassLoader())); 90 WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder")); 91 intHolder.get().getDeclaredMethod("runGC").invoke(intHolder.get()); 92 boolean isNull = loader.get() == null; 93 System.out.println("loader null " + isNull); 94 } 95 96 private static void testNoUnloadInstance(Constructor constructor) throws Exception { 97 WeakReference<ClassLoader> loader = 98 new WeakReference((ClassLoader) constructor.newInstance( 99 DEX_FILE, ClassLoader.getSystemClassLoader())); 100 WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder")); 101 Object o = intHolder.get().newInstance(); 102 Runtime.getRuntime().gc(); 103 boolean isNull = loader.get() == null; 104 System.out.println("loader null " + isNull); 105 } 106 107 private static WeakReference<Class> setUpUnloadClass(Constructor constructor) throws Exception { 108 ClassLoader loader = (ClassLoader) constructor.newInstance( 109 DEX_FILE, ClassLoader.getSystemClassLoader()); 110 Class intHolder = loader.loadClass("IntHolder"); 111 Method getValue = intHolder.getDeclaredMethod("getValue"); 112 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE); 113 // Make sure we don't accidentally preserve the value in the int holder, the class 114 // initializer should be re-run. 115 System.out.println((int) getValue.invoke(intHolder)); 116 setValue.invoke(intHolder, 2); 117 System.out.println((int) getValue.invoke(intHolder)); 118 waitForCompilation(intHolder); 119 return new WeakReference(intHolder); 120 } 121 122 private static WeakReference<ClassLoader> setUpUnloadLoader(Constructor constructor, 123 boolean waitForCompilation) 124 throws Exception { 125 ClassLoader loader = (ClassLoader) constructor.newInstance( 126 DEX_FILE, ClassLoader.getSystemClassLoader()); 127 Class intHolder = loader.loadClass("IntHolder"); 128 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE); 129 setValue.invoke(intHolder, 2); 130 if (waitForCompilation) { 131 waitForCompilation(intHolder); 132 } 133 return new WeakReference(loader); 134 } 135 136 private static void waitForCompilation(Class intHolder) throws Exception { 137 // Load the native library so that we can call waitForCompilation. 138 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class); 139 loadLibrary.invoke(intHolder, nativeLibraryName); 140 // Wait for JIT compilation to finish since the async threads may prevent unloading. 141 Method waitForCompilation = intHolder.getDeclaredMethod("waitForCompilation"); 142 waitForCompilation.invoke(intHolder); 143 } 144 145 private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor constructor) 146 throws Exception { 147 ClassLoader loader = (ClassLoader) constructor.newInstance( 148 DEX_FILE, ClassLoader.getSystemClassLoader()); 149 Class intHolder = loader.loadClass("IntHolder"); 150 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class); 151 loadLibrary.invoke(intHolder, nativeLibraryName); 152 return new WeakReference(loader); 153 } 154} 155