Main.java revision 40d4c7636e51f910b2c9ef226b7183e6ccc9ab4b
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.io.BufferedReader; 18import java.io.File; 19import java.io.FileReader; 20import java.lang.ref.WeakReference; 21import java.lang.reflect.Constructor; 22import java.lang.reflect.Method; 23 24public class Main { 25 static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/141-class-unload-ex.jar"; 26 static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path"); 27 static String nativeLibraryName; 28 29 public static void main(String[] args) throws Exception { 30 nativeLibraryName = args[0]; 31 Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); 32 if (pathClassLoader == null) { 33 throw new AssertionError("Couldn't find path class loader class"); 34 } 35 Constructor constructor = 36 pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class); 37 try { 38 testUnloadClass(constructor); 39 testUnloadLoader(constructor); 40 // Test that we don't unload if we have a Method keeping the class live. 41 testNoUnloadInvoke(constructor); 42 // Test that we don't unload if we have an instance. 43 testNoUnloadInstance(constructor); 44 // Test JNI_OnLoad and JNI_OnUnload. 45 testLoadAndUnloadLibrary(constructor); 46 // Test that stack traces keep the classes live. 47 testStackTrace(constructor); 48 // Stress test to make sure we dont leak memory. 49 stressTest(constructor); 50 // Test that the oat files are unloaded. 51 testOatFilesUnloaded(getPid()); 52 // Test that objects keep class loader live for sticky GC. 53 testStickyUnload(constructor); 54 } catch (Exception e) { 55 e.printStackTrace(); 56 } 57 } 58 59 private static void testOatFilesUnloaded(int pid) throws Exception { 60 BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps")); 61 String line; 62 int count = 0; 63 Runtime.getRuntime().gc(); 64 System.runFinalization(); 65 while ((line = reader.readLine()) != null) { 66 if (line.contains("@141-class-unload-ex.jar")) { 67 System.out.println(line); 68 ++count; 69 } 70 } 71 System.out.println("Number of loaded unload-ex maps " + count); 72 } 73 74 private static void stressTest(Constructor constructor) throws Exception { 75 for (int i = 0; i <= 100; ++i) { 76 setUpUnloadLoader(constructor, false); 77 if (i % 10 == 0) { 78 Runtime.getRuntime().gc(); 79 } 80 } 81 } 82 83 private static void testUnloadClass(Constructor constructor) throws Exception { 84 WeakReference<Class> klass = setUpUnloadClass(constructor); 85 // No strong references to class loader, should get unloaded. 86 Runtime.getRuntime().gc(); 87 WeakReference<Class> klass2 = setUpUnloadClass(constructor); 88 Runtime.getRuntime().gc(); 89 // If the weak reference is cleared, then it was unloaded. 90 System.out.println(klass.get()); 91 System.out.println(klass2.get()); 92 } 93 94 private static void testUnloadLoader(Constructor constructor) 95 throws Exception { 96 WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true); 97 // No strong references to class loader, should get unloaded. 98 Runtime.getRuntime().gc(); 99 // If the weak reference is cleared, then it was unloaded. 100 System.out.println(loader.get()); 101 } 102 103 private static void testStackTrace(Constructor constructor) throws Exception { 104 WeakReference<Class> klass = setUpUnloadClass(constructor); 105 Method stackTraceMethod = klass.get().getDeclaredMethod("generateStackTrace"); 106 Throwable throwable = (Throwable) stackTraceMethod.invoke(klass.get()); 107 stackTraceMethod = null; 108 Runtime.getRuntime().gc(); 109 boolean isNull = klass.get() == null; 110 System.out.println("class null " + isNull + " " + throwable.getMessage()); 111 } 112 113 private static void testLoadAndUnloadLibrary(Constructor constructor) throws Exception { 114 WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor); 115 // No strong references to class loader, should get unloaded. 116 Runtime.getRuntime().gc(); 117 // If the weak reference is cleared, then it was unloaded. 118 System.out.println(loader.get()); 119 } 120 121 private static void testNoUnloadInvoke(Constructor constructor) throws Exception { 122 WeakReference<ClassLoader> loader = 123 new WeakReference((ClassLoader) constructor.newInstance( 124 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader())); 125 WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder")); 126 intHolder.get().getDeclaredMethod("runGC").invoke(intHolder.get()); 127 boolean isNull = loader.get() == null; 128 System.out.println("loader null " + isNull); 129 } 130 131 private static void testNoUnloadInstance(Constructor constructor) throws Exception { 132 WeakReference<ClassLoader> loader = 133 new WeakReference((ClassLoader) constructor.newInstance( 134 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader())); 135 WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder")); 136 Object o = intHolder.get().newInstance(); 137 Runtime.getRuntime().gc(); 138 boolean isNull = loader.get() == null; 139 System.out.println("loader null " + isNull); 140 } 141 142 private static WeakReference<Class> setUpUnloadClass(Constructor constructor) throws Exception { 143 ClassLoader loader = (ClassLoader) constructor.newInstance( 144 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); 145 Class intHolder = loader.loadClass("IntHolder"); 146 Method getValue = intHolder.getDeclaredMethod("getValue"); 147 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE); 148 // Make sure we don't accidentally preserve the value in the int holder, the class 149 // initializer should be re-run. 150 System.out.println((int) getValue.invoke(intHolder)); 151 setValue.invoke(intHolder, 2); 152 System.out.println((int) getValue.invoke(intHolder)); 153 waitForCompilation(intHolder); 154 return new WeakReference(intHolder); 155 } 156 157 private static Object allocObjectInOtherClassLoader(Constructor<?> constructor) 158 throws Exception { 159 ClassLoader loader = (ClassLoader) constructor.newInstance( 160 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); 161 return loader.loadClass("IntHolder").newInstance(); 162 } 163 164 // Regression test for public issue 227182. 165 private static void testStickyUnload(Constructor<?> constructor) throws Exception { 166 String s = ""; 167 for (int i = 0; i < 10; ++i) { 168 s = ""; 169 // The object is the only thing preventing the class loader from being unloaded. 170 Object o = allocObjectInOtherClassLoader(constructor); 171 for (int j = 0; j < 1000; ++j) { 172 s += j + " "; 173 } 174 // Make sure the object still has a valid class (hasn't been incorrectly unloaded). 175 s += o.getClass().getName(); 176 o = null; 177 } 178 System.out.println("Too small " + (s.length() < 1000)); 179 } 180 181 private static WeakReference<ClassLoader> setUpUnloadLoader(Constructor constructor, 182 boolean waitForCompilation) 183 throws Exception { 184 ClassLoader loader = (ClassLoader) constructor.newInstance( 185 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); 186 Class intHolder = loader.loadClass("IntHolder"); 187 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE); 188 setValue.invoke(intHolder, 2); 189 if (waitForCompilation) { 190 waitForCompilation(intHolder); 191 } 192 return new WeakReference(loader); 193 } 194 195 private static void waitForCompilation(Class intHolder) throws Exception { 196 // Load the native library so that we can call waitForCompilation. 197 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class); 198 loadLibrary.invoke(intHolder, nativeLibraryName); 199 // Wait for JIT compilation to finish since the async threads may prevent unloading. 200 Method waitForCompilation = intHolder.getDeclaredMethod("waitForCompilation"); 201 waitForCompilation.invoke(intHolder); 202 } 203 204 private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor constructor) 205 throws Exception { 206 ClassLoader loader = (ClassLoader) constructor.newInstance( 207 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); 208 Class intHolder = loader.loadClass("IntHolder"); 209 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class); 210 loadLibrary.invoke(intHolder, nativeLibraryName); 211 waitForCompilation(intHolder); 212 return new WeakReference(loader); 213 } 214 215 private static int getPid() throws Exception { 216 return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName()); 217 } 218} 219