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 17 import java.io.BufferedReader; 18 import java.io.File; 19 import java.io.FileReader; 20 import java.lang.ref.WeakReference; 21 import java.lang.reflect.Constructor; 22 import java.lang.reflect.Method; 23 24 public 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 main(String[] args)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 an instance. 41 testNoUnloadInstance(constructor); 42 // Test JNI_OnLoad and JNI_OnUnload. 43 testLoadAndUnloadLibrary(constructor); 44 // Test that stack traces keep the classes live. 45 testStackTrace(constructor); 46 // Stress test to make sure we dont leak memory. 47 stressTest(constructor); 48 // Test that the oat files are unloaded. 49 testOatFilesUnloaded(getPid()); 50 // Test that objects keep class loader live for sticky GC. 51 testStickyUnload(constructor); 52 } catch (Exception e) { 53 e.printStackTrace(System.out); 54 } 55 } 56 testOatFilesUnloaded(int pid)57 private static void testOatFilesUnloaded(int pid) throws Exception { 58 System.loadLibrary(nativeLibraryName); 59 // Stop the JIT to ensure its threads and work queue are not keeping classes 60 // artifically alive. 61 stopJit(); 62 doUnloading(); 63 System.runFinalization(); 64 BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps")); 65 String line; 66 int count = 0; 67 while ((line = reader.readLine()) != null) { 68 if (line.contains("141-class-unload-ex.odex") || 69 line.contains("141-class-unload-ex.vdex")) { 70 System.out.println(line); 71 ++count; 72 } 73 } 74 System.out.println("Number of loaded unload-ex maps " + count); 75 startJit(); 76 } 77 stressTest(Constructor<?> constructor)78 private static void stressTest(Constructor<?> constructor) throws Exception { 79 for (int i = 0; i <= 100; ++i) { 80 setUpUnloadLoader(constructor, false); 81 if (i % 10 == 0) { 82 Runtime.getRuntime().gc(); 83 } 84 } 85 } 86 doUnloading()87 private static void doUnloading() { 88 // Do multiple GCs to prevent rare flakiness if some other thread is keeping the 89 // classloader live. 90 for (int i = 0; i < 5; ++i) { 91 Runtime.getRuntime().gc(); 92 } 93 } 94 testUnloadClass(Constructor<?> constructor)95 private static void testUnloadClass(Constructor<?> constructor) throws Exception { 96 WeakReference<Class> klass = setUpUnloadClassWeak(constructor); 97 // No strong references to class loader, should get unloaded. 98 doUnloading(); 99 WeakReference<Class> klass2 = setUpUnloadClassWeak(constructor); 100 doUnloading(); 101 // If the weak reference is cleared, then it was unloaded. 102 System.out.println(klass.get()); 103 System.out.println(klass2.get()); 104 } 105 testUnloadLoader(Constructor<?> constructor)106 private static void testUnloadLoader(Constructor<?> constructor) 107 throws Exception { 108 WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true); 109 // No strong references to class loader, should get unloaded. 110 doUnloading(); 111 // If the weak reference is cleared, then it was unloaded. 112 System.out.println(loader.get()); 113 } 114 testStackTrace(Constructor<?> constructor)115 private static void testStackTrace(Constructor<?> constructor) throws Exception { 116 Class<?> klass = setUpUnloadClass(constructor); 117 WeakReference<Class> weak_klass = new WeakReference(klass); 118 Method stackTraceMethod = klass.getDeclaredMethod("generateStackTrace"); 119 Throwable throwable = (Throwable) stackTraceMethod.invoke(klass); 120 stackTraceMethod = null; 121 klass = null; 122 doUnloading(); 123 boolean isNull = weak_klass.get() == null; 124 System.out.println("class null " + isNull + " " + throwable.getMessage()); 125 } 126 testLoadAndUnloadLibrary(Constructor<?> constructor)127 private static void testLoadAndUnloadLibrary(Constructor<?> constructor) throws Exception { 128 WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor); 129 // No strong references to class loader, should get unloaded. 130 doUnloading(); 131 // If the weak reference is cleared, then it was unloaded. 132 System.out.println(loader.get()); 133 } 134 testNoUnloadHelper(ClassLoader loader)135 private static Object testNoUnloadHelper(ClassLoader loader) throws Exception { 136 Class<?> intHolder = loader.loadClass("IntHolder"); 137 return intHolder.newInstance(); 138 } 139 140 static class Pair { Pair(Object o, ClassLoader l)141 public Pair(Object o, ClassLoader l) { 142 object = o; 143 classLoader = new WeakReference<ClassLoader>(l); 144 } 145 146 public Object object; 147 public WeakReference<ClassLoader> classLoader; 148 } 149 testNoUnloadInstanceHelper(Constructor<?> constructor)150 private static Pair testNoUnloadInstanceHelper(Constructor<?> constructor) throws Exception { 151 ClassLoader loader = (ClassLoader) constructor.newInstance( 152 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); 153 Object o = testNoUnloadHelper(loader); 154 return new Pair(o, loader); 155 } 156 testNoUnloadInstance(Constructor<?> constructor)157 private static void testNoUnloadInstance(Constructor<?> constructor) throws Exception { 158 Pair p = testNoUnloadInstanceHelper(constructor); 159 doUnloading(); 160 // If the class loader was unloded too early due to races, just pass the test. 161 boolean isNull = p.classLoader.get() == null; 162 System.out.println("loader null " + isNull); 163 } 164 setUpUnloadClass(Constructor<?> constructor)165 private static Class<?> setUpUnloadClass(Constructor<?> constructor) throws Exception { 166 ClassLoader loader = (ClassLoader) constructor.newInstance( 167 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); 168 Class<?> intHolder = loader.loadClass("IntHolder"); 169 Method getValue = intHolder.getDeclaredMethod("getValue"); 170 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE); 171 // Make sure we don't accidentally preserve the value in the int holder, the class 172 // initializer should be re-run. 173 System.out.println((int) getValue.invoke(intHolder)); 174 setValue.invoke(intHolder, 2); 175 System.out.println((int) getValue.invoke(intHolder)); 176 waitForCompilation(intHolder); 177 return intHolder; 178 } 179 allocObjectInOtherClassLoader(Constructor<?> constructor)180 private static Object allocObjectInOtherClassLoader(Constructor<?> constructor) 181 throws Exception { 182 ClassLoader loader = (ClassLoader) constructor.newInstance( 183 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); 184 return loader.loadClass("IntHolder").newInstance(); 185 } 186 187 // Regression test for public issue 227182. testStickyUnload(Constructor<?> constructor)188 private static void testStickyUnload(Constructor<?> constructor) throws Exception { 189 String s = ""; 190 for (int i = 0; i < 10; ++i) { 191 s = ""; 192 // The object is the only thing preventing the class loader from being unloaded. 193 Object o = allocObjectInOtherClassLoader(constructor); 194 for (int j = 0; j < 1000; ++j) { 195 s += j + " "; 196 } 197 // Make sure the object still has a valid class (hasn't been incorrectly unloaded). 198 s += o.getClass().getName(); 199 o = null; 200 } 201 System.out.println("Too small " + (s.length() < 1000)); 202 } 203 setUpUnloadClassWeak(Constructor<?> constructor)204 private static WeakReference<Class> setUpUnloadClassWeak(Constructor<?> constructor) 205 throws Exception { 206 return new WeakReference<Class>(setUpUnloadClass(constructor)); 207 } 208 setUpUnloadLoader(Constructor<?> constructor, boolean waitForCompilation)209 private static WeakReference<ClassLoader> setUpUnloadLoader(Constructor<?> constructor, 210 boolean waitForCompilation) 211 throws Exception { 212 ClassLoader loader = (ClassLoader) constructor.newInstance( 213 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); 214 Class<?> intHolder = loader.loadClass("IntHolder"); 215 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE); 216 setValue.invoke(intHolder, 2); 217 if (waitForCompilation) { 218 waitForCompilation(intHolder); 219 } 220 return new WeakReference(loader); 221 } 222 waitForCompilation(Class<?> intHolder)223 private static void waitForCompilation(Class<?> intHolder) throws Exception { 224 // Load the native library so that we can call waitForCompilation. 225 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class); 226 loadLibrary.invoke(intHolder, nativeLibraryName); 227 // Wait for JIT compilation to finish since the async threads may prevent unloading. 228 Method waitForCompilation = intHolder.getDeclaredMethod("waitForCompilation"); 229 waitForCompilation.invoke(intHolder); 230 } 231 setUpLoadLibrary(Constructor<?> constructor)232 private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor<?> constructor) 233 throws Exception { 234 ClassLoader loader = (ClassLoader) constructor.newInstance( 235 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); 236 Class<?> intHolder = loader.loadClass("IntHolder"); 237 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class); 238 loadLibrary.invoke(intHolder, nativeLibraryName); 239 waitForCompilation(intHolder); 240 return new WeakReference(loader); 241 } 242 getPid()243 private static int getPid() throws Exception { 244 return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName()); 245 } 246 stopJit()247 public static native void stopJit(); startJit()248 public static native void startJit(); 249 } 250