blob: 9ed8d28a026b1921769b5f1c8e38f551ef9d8c68 [file] [log] [blame]
Mathieu Chartier05aa4d32015-09-19 12:44:38 -07001/*
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
Mathieu Chartiere58991b2015-10-13 07:59:34 -070017import java.io.BufferedReader;
18import java.io.File;
19import java.io.FileReader;
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070020import 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";
Dimitry Ivanov3a79b632016-04-28 16:29:22 -070026 static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
Mathieu Chartier598302a2015-09-23 14:52:39 -070027 static String nativeLibraryName;
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070028
29 public static void main(String[] args) throws Exception {
Mathieu Chartier598302a2015-09-23 14:52:39 -070030 nativeLibraryName = args[0];
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070031 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 =
Dimitry Ivanov3a79b632016-04-28 16:29:22 -070036 pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070037 try {
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070038 testUnloadClass(constructor);
39 testUnloadLoader(constructor);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070040 // Test that we don't unload if we have an instance.
41 testNoUnloadInstance(constructor);
Mathieu Chartier598302a2015-09-23 14:52:39 -070042 // Test JNI_OnLoad and JNI_OnUnload.
43 testLoadAndUnloadLibrary(constructor);
Mathieu Chartier910e8272015-09-30 09:24:22 -070044 // Test that stack traces keep the classes live.
45 testStackTrace(constructor);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070046 // Stress test to make sure we dont leak memory.
47 stressTest(constructor);
Mathieu Chartiere58991b2015-10-13 07:59:34 -070048 // Test that the oat files are unloaded.
49 testOatFilesUnloaded(getPid());
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070050 } catch (Exception e) {
Dimitry Ivanov3a79b632016-04-28 16:29:22 -070051 e.printStackTrace();
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070052 }
53 }
54
Mathieu Chartiere58991b2015-10-13 07:59:34 -070055 private static void testOatFilesUnloaded(int pid) throws Exception {
56 BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps"));
57 String line;
58 int count = 0;
59 Runtime.getRuntime().gc();
60 System.runFinalization();
61 while ((line = reader.readLine()) != null) {
62 if (line.contains("@141-class-unload-ex.jar")) {
63 System.out.println(line);
64 ++count;
65 }
66 }
67 System.out.println("Number of loaded unload-ex maps " + count);
68 }
69
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070070 private static void stressTest(Constructor constructor) throws Exception {
71 for (int i = 0; i <= 100; ++i) {
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070072 setUpUnloadLoader(constructor, false);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070073 if (i % 10 == 0) {
74 Runtime.getRuntime().gc();
75 }
76 }
77 }
78
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070079 private static void testUnloadClass(Constructor constructor) throws Exception {
Mathieu Chartierb0e861c2016-06-30 15:20:26 -070080 WeakReference<Class> klass = setUpUnloadClassWeak(constructor);
Roland Levillain91d65e02016-01-19 15:59:16 +000081 // No strong references to class loader, should get unloaded.
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070082 Runtime.getRuntime().gc();
Mathieu Chartierb0e861c2016-06-30 15:20:26 -070083 WeakReference<Class> klass2 = setUpUnloadClassWeak(constructor);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070084 Runtime.getRuntime().gc();
85 // If the weak reference is cleared, then it was unloaded.
86 System.out.println(klass.get());
87 System.out.println(klass2.get());
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070088 }
89
90 private static void testUnloadLoader(Constructor constructor)
91 throws Exception {
92 WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true);
Roland Levillain91d65e02016-01-19 15:59:16 +000093 // No strong references to class loader, should get unloaded.
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070094 Runtime.getRuntime().gc();
95 // If the weak reference is cleared, then it was unloaded.
96 System.out.println(loader.get());
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070097 }
98
Mathieu Chartier910e8272015-09-30 09:24:22 -070099 private static void testStackTrace(Constructor constructor) throws Exception {
Mathieu Chartierb0e861c2016-06-30 15:20:26 -0700100 Class klass = setUpUnloadClass(constructor);
101 WeakReference<Class> weak_klass = new WeakReference(klass);
102 Method stackTraceMethod = klass.getDeclaredMethod("generateStackTrace");
103 Throwable throwable = (Throwable) stackTraceMethod.invoke(klass);
Mathieu Chartier910e8272015-09-30 09:24:22 -0700104 stackTraceMethod = null;
Mathieu Chartierb0e861c2016-06-30 15:20:26 -0700105 klass = null;
Mathieu Chartier910e8272015-09-30 09:24:22 -0700106 Runtime.getRuntime().gc();
Mathieu Chartierb0e861c2016-06-30 15:20:26 -0700107 boolean isNull = weak_klass.get() == null;
Mathieu Chartier910e8272015-09-30 09:24:22 -0700108 System.out.println("class null " + isNull + " " + throwable.getMessage());
109 }
110
Mathieu Chartier598302a2015-09-23 14:52:39 -0700111 private static void testLoadAndUnloadLibrary(Constructor constructor) throws Exception {
112 WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor);
Roland Levillain91d65e02016-01-19 15:59:16 +0000113 // No strong references to class loader, should get unloaded.
Mathieu Chartier598302a2015-09-23 14:52:39 -0700114 Runtime.getRuntime().gc();
115 // If the weak reference is cleared, then it was unloaded.
116 System.out.println(loader.get());
117 }
118
Mathieu Chartierb0e861c2016-06-30 15:20:26 -0700119 private static Object testNoUnloadHelper(ClassLoader loader) throws Exception {
120 Class intHolder = loader.loadClass("IntHolder");
121 return intHolder.newInstance();
122 }
123
124 static class Pair {
125 public Pair(Object o, ClassLoader l) {
126 object = o;
127 classLoader = new WeakReference<ClassLoader>(l);
128 }
129
130 public Object object;
131 public WeakReference<ClassLoader> classLoader;
132 }
133
134 private static Pair testNoUnloadInstanceHelper(Constructor constructor) throws Exception {
135 ClassLoader loader = (ClassLoader) constructor.newInstance(
136 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
137 Object o = testNoUnloadHelper(loader);
138 return new Pair(o, loader);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700139 }
140
141 private static void testNoUnloadInstance(Constructor constructor) throws Exception {
Mathieu Chartierb0e861c2016-06-30 15:20:26 -0700142 Pair p = testNoUnloadInstanceHelper(constructor);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700143 Runtime.getRuntime().gc();
Mathieu Chartierb0e861c2016-06-30 15:20:26 -0700144 // If the class loader was unloded too early due to races, just pass the test.
145 boolean isNull = p.classLoader.get() == null;
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700146 System.out.println("loader null " + isNull);
147 }
148
Mathieu Chartierb0e861c2016-06-30 15:20:26 -0700149 private static Class setUpUnloadClass(Constructor constructor) throws Exception {
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700150 ClassLoader loader = (ClassLoader) constructor.newInstance(
Dimitry Ivanov3a79b632016-04-28 16:29:22 -0700151 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700152 Class intHolder = loader.loadClass("IntHolder");
153 Method getValue = intHolder.getDeclaredMethod("getValue");
154 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
155 // Make sure we don't accidentally preserve the value in the int holder, the class
156 // initializer should be re-run.
157 System.out.println((int) getValue.invoke(intHolder));
158 setValue.invoke(intHolder, 2);
159 System.out.println((int) getValue.invoke(intHolder));
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700160 waitForCompilation(intHolder);
Mathieu Chartierb0e861c2016-06-30 15:20:26 -0700161 return intHolder;
162 }
163
164 private static WeakReference<Class> setUpUnloadClassWeak(Constructor constructor)
165 throws Exception {
166 return new WeakReference<Class>(setUpUnloadClass(constructor));
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700167 }
168
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700169 private static WeakReference<ClassLoader> setUpUnloadLoader(Constructor constructor,
170 boolean waitForCompilation)
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700171 throws Exception {
172 ClassLoader loader = (ClassLoader) constructor.newInstance(
Dimitry Ivanov3a79b632016-04-28 16:29:22 -0700173 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700174 Class intHolder = loader.loadClass("IntHolder");
175 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
176 setValue.invoke(intHolder, 2);
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700177 if (waitForCompilation) {
178 waitForCompilation(intHolder);
179 }
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700180 return new WeakReference(loader);
181 }
182
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700183 private static void waitForCompilation(Class intHolder) throws Exception {
184 // Load the native library so that we can call waitForCompilation.
185 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
186 loadLibrary.invoke(intHolder, nativeLibraryName);
187 // Wait for JIT compilation to finish since the async threads may prevent unloading.
188 Method waitForCompilation = intHolder.getDeclaredMethod("waitForCompilation");
189 waitForCompilation.invoke(intHolder);
190 }
191
Mathieu Chartier598302a2015-09-23 14:52:39 -0700192 private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor constructor)
193 throws Exception {
194 ClassLoader loader = (ClassLoader) constructor.newInstance(
Dimitry Ivanov3a79b632016-04-28 16:29:22 -0700195 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
Mathieu Chartier598302a2015-09-23 14:52:39 -0700196 Class intHolder = loader.loadClass("IntHolder");
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700197 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
198 loadLibrary.invoke(intHolder, nativeLibraryName);
Nicolas Geoffrayb6e20ae2016-03-07 14:29:04 +0000199 waitForCompilation(intHolder);
Mathieu Chartier598302a2015-09-23 14:52:39 -0700200 return new WeakReference(loader);
201 }
Mathieu Chartiere58991b2015-10-13 07:59:34 -0700202
203 private static int getPid() throws Exception {
204 return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
205 }
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700206}