blob: 15683b0b1ef199ff504aa47ec2e23bee361672ce [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";
Mathieu Chartier598302a2015-09-23 14:52:39 -070026 static String nativeLibraryName;
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070027
28 public static void main(String[] args) throws Exception {
Mathieu Chartier598302a2015-09-23 14:52:39 -070029 nativeLibraryName = args[0];
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070030 Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
31 if (pathClassLoader == null) {
32 throw new AssertionError("Couldn't find path class loader class");
33 }
34 Constructor constructor =
35 pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
36 try {
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070037 testUnloadClass(constructor);
38 testUnloadLoader(constructor);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070039 // Test that we don't unload if we have a Method keeping the class live.
40 testNoUnloadInvoke(constructor);
41 // Test that we don't unload if we have an instance.
42 testNoUnloadInstance(constructor);
Mathieu Chartier598302a2015-09-23 14:52:39 -070043 // Test JNI_OnLoad and JNI_OnUnload.
44 testLoadAndUnloadLibrary(constructor);
Mathieu Chartier910e8272015-09-30 09:24:22 -070045 // Test that stack traces keep the classes live.
46 testStackTrace(constructor);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070047 // Stress test to make sure we dont leak memory.
48 stressTest(constructor);
Mathieu Chartiere58991b2015-10-13 07:59:34 -070049 // Test that the oat files are unloaded.
50 testOatFilesUnloaded(getPid());
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070051 } catch (Exception e) {
52 System.out.println(e);
53 }
54 }
55
Mathieu Chartiere58991b2015-10-13 07:59:34 -070056 private static void testOatFilesUnloaded(int pid) throws Exception {
57 BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps"));
58 String line;
59 int count = 0;
60 Runtime.getRuntime().gc();
61 System.runFinalization();
62 while ((line = reader.readLine()) != null) {
63 if (line.contains("@141-class-unload-ex.jar")) {
64 System.out.println(line);
65 ++count;
66 }
67 }
68 System.out.println("Number of loaded unload-ex maps " + count);
69 }
70
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070071 private static void stressTest(Constructor constructor) throws Exception {
72 for (int i = 0; i <= 100; ++i) {
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070073 setUpUnloadLoader(constructor, false);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070074 if (i % 10 == 0) {
75 Runtime.getRuntime().gc();
76 }
77 }
78 }
79
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070080 private static void testUnloadClass(Constructor constructor) throws Exception {
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070081 WeakReference<Class> klass = setUpUnloadClass(constructor);
Roland Levillain91d65e02016-01-19 15:59:16 +000082 // No strong references to class loader, should get unloaded.
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070083 Runtime.getRuntime().gc();
84 WeakReference<Class> klass2 = setUpUnloadClass(constructor);
85 Runtime.getRuntime().gc();
86 // If the weak reference is cleared, then it was unloaded.
87 System.out.println(klass.get());
88 System.out.println(klass2.get());
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070089 }
90
91 private static void testUnloadLoader(Constructor constructor)
92 throws Exception {
93 WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true);
Roland Levillain91d65e02016-01-19 15:59:16 +000094 // No strong references to class loader, should get unloaded.
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070095 Runtime.getRuntime().gc();
96 // If the weak reference is cleared, then it was unloaded.
97 System.out.println(loader.get());
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070098 }
99
Mathieu Chartier910e8272015-09-30 09:24:22 -0700100 private static void testStackTrace(Constructor constructor) throws Exception {
101 WeakReference<Class> klass = setUpUnloadClass(constructor);
102 Method stackTraceMethod = klass.get().getDeclaredMethod("generateStackTrace");
103 Throwable throwable = (Throwable) stackTraceMethod.invoke(klass.get());
104 stackTraceMethod = null;
105 Runtime.getRuntime().gc();
106 boolean isNull = klass.get() == null;
107 System.out.println("class null " + isNull + " " + throwable.getMessage());
108 }
109
Mathieu Chartier598302a2015-09-23 14:52:39 -0700110 private static void testLoadAndUnloadLibrary(Constructor constructor) throws Exception {
111 WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor);
Roland Levillain91d65e02016-01-19 15:59:16 +0000112 // No strong references to class loader, should get unloaded.
Mathieu Chartier598302a2015-09-23 14:52:39 -0700113 Runtime.getRuntime().gc();
114 // If the weak reference is cleared, then it was unloaded.
115 System.out.println(loader.get());
116 }
117
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700118 private static void testNoUnloadInvoke(Constructor constructor) throws Exception {
119 WeakReference<ClassLoader> loader =
120 new WeakReference((ClassLoader) constructor.newInstance(
121 DEX_FILE, ClassLoader.getSystemClassLoader()));
122 WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
123 intHolder.get().getDeclaredMethod("runGC").invoke(intHolder.get());
124 boolean isNull = loader.get() == null;
125 System.out.println("loader null " + isNull);
126 }
127
128 private static void testNoUnloadInstance(Constructor constructor) throws Exception {
129 WeakReference<ClassLoader> loader =
130 new WeakReference((ClassLoader) constructor.newInstance(
131 DEX_FILE, ClassLoader.getSystemClassLoader()));
132 WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
133 Object o = intHolder.get().newInstance();
134 Runtime.getRuntime().gc();
135 boolean isNull = loader.get() == null;
136 System.out.println("loader null " + isNull);
137 }
138
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700139 private static WeakReference<Class> setUpUnloadClass(Constructor constructor) throws Exception {
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700140 ClassLoader loader = (ClassLoader) constructor.newInstance(
141 DEX_FILE, ClassLoader.getSystemClassLoader());
142 Class intHolder = loader.loadClass("IntHolder");
143 Method getValue = intHolder.getDeclaredMethod("getValue");
144 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
145 // Make sure we don't accidentally preserve the value in the int holder, the class
146 // initializer should be re-run.
147 System.out.println((int) getValue.invoke(intHolder));
148 setValue.invoke(intHolder, 2);
149 System.out.println((int) getValue.invoke(intHolder));
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700150 waitForCompilation(intHolder);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700151 return new WeakReference(intHolder);
152 }
153
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700154 private static WeakReference<ClassLoader> setUpUnloadLoader(Constructor constructor,
155 boolean waitForCompilation)
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700156 throws Exception {
157 ClassLoader loader = (ClassLoader) constructor.newInstance(
158 DEX_FILE, ClassLoader.getSystemClassLoader());
159 Class intHolder = loader.loadClass("IntHolder");
160 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
161 setValue.invoke(intHolder, 2);
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700162 if (waitForCompilation) {
163 waitForCompilation(intHolder);
164 }
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700165 return new WeakReference(loader);
166 }
167
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700168 private static void waitForCompilation(Class intHolder) throws Exception {
169 // Load the native library so that we can call waitForCompilation.
170 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
171 loadLibrary.invoke(intHolder, nativeLibraryName);
172 // Wait for JIT compilation to finish since the async threads may prevent unloading.
173 Method waitForCompilation = intHolder.getDeclaredMethod("waitForCompilation");
174 waitForCompilation.invoke(intHolder);
175 }
176
Mathieu Chartier598302a2015-09-23 14:52:39 -0700177 private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor constructor)
178 throws Exception {
179 ClassLoader loader = (ClassLoader) constructor.newInstance(
180 DEX_FILE, ClassLoader.getSystemClassLoader());
181 Class intHolder = loader.loadClass("IntHolder");
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700182 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
183 loadLibrary.invoke(intHolder, nativeLibraryName);
Nicolas Geoffrayb6e20ae2016-03-07 14:29:04 +0000184 waitForCompilation(intHolder);
Mathieu Chartier598302a2015-09-23 14:52:39 -0700185 return new WeakReference(loader);
186 }
Mathieu Chartiere58991b2015-10-13 07:59:34 -0700187
188 private static int getPid() throws Exception {
189 return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
190 }
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700191}