blob: 3cc43accbee9bd9667f0408593056c3c2358b142 [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
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";
Mathieu Chartier598302a2015-09-23 14:52:39 -070023 static String nativeLibraryName;
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070024
25 public static void main(String[] args) throws Exception {
Mathieu Chartier598302a2015-09-23 14:52:39 -070026 nativeLibraryName = args[0];
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070027 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 {
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070034 testUnloadClass(constructor);
35 testUnloadLoader(constructor);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070036 // 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);
Mathieu Chartier598302a2015-09-23 14:52:39 -070040 // Test JNI_OnLoad and JNI_OnUnload.
41 testLoadAndUnloadLibrary(constructor);
Mathieu Chartier910e8272015-09-30 09:24:22 -070042 // Test that stack traces keep the classes live.
43 testStackTrace(constructor);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070044 // Stress test to make sure we dont leak memory.
45 stressTest(constructor);
46 } catch (Exception e) {
47 System.out.println(e);
48 }
49 }
50
51 private static void stressTest(Constructor constructor) throws Exception {
52 for (int i = 0; i <= 100; ++i) {
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070053 setUpUnloadLoader(constructor, false);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070054 if (i % 10 == 0) {
55 Runtime.getRuntime().gc();
56 }
57 }
58 }
59
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070060 private static void testUnloadClass(Constructor constructor) throws Exception {
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070061 WeakReference<Class> klass = setUpUnloadClass(constructor);
62 // No strong refernces to class loader, should get unloaded.
63 Runtime.getRuntime().gc();
64 WeakReference<Class> klass2 = setUpUnloadClass(constructor);
65 Runtime.getRuntime().gc();
66 // If the weak reference is cleared, then it was unloaded.
67 System.out.println(klass.get());
68 System.out.println(klass2.get());
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070069 }
70
71 private static void testUnloadLoader(Constructor constructor)
72 throws Exception {
73 WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true);
74 // No strong refernces to class loader, should get unloaded.
75 Runtime.getRuntime().gc();
76 // If the weak reference is cleared, then it was unloaded.
77 System.out.println(loader.get());
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070078 }
79
Mathieu Chartier910e8272015-09-30 09:24:22 -070080 private static void testStackTrace(Constructor constructor) throws Exception {
81 WeakReference<Class> klass = setUpUnloadClass(constructor);
82 Method stackTraceMethod = klass.get().getDeclaredMethod("generateStackTrace");
83 Throwable throwable = (Throwable) stackTraceMethod.invoke(klass.get());
84 stackTraceMethod = null;
85 Runtime.getRuntime().gc();
86 boolean isNull = klass.get() == null;
87 System.out.println("class null " + isNull + " " + throwable.getMessage());
88 }
89
Mathieu Chartier598302a2015-09-23 14:52:39 -070090 private static void testLoadAndUnloadLibrary(Constructor constructor) throws Exception {
91 WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor);
92 // No strong refernces to class loader, should get unloaded.
93 Runtime.getRuntime().gc();
94 // If the weak reference is cleared, then it was unloaded.
95 System.out.println(loader.get());
96 }
97
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070098 private static void testNoUnloadInvoke(Constructor constructor) throws Exception {
99 WeakReference<ClassLoader> loader =
100 new WeakReference((ClassLoader) constructor.newInstance(
101 DEX_FILE, ClassLoader.getSystemClassLoader()));
102 WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
103 intHolder.get().getDeclaredMethod("runGC").invoke(intHolder.get());
104 boolean isNull = loader.get() == null;
105 System.out.println("loader null " + isNull);
106 }
107
108 private static void testNoUnloadInstance(Constructor constructor) throws Exception {
109 WeakReference<ClassLoader> loader =
110 new WeakReference((ClassLoader) constructor.newInstance(
111 DEX_FILE, ClassLoader.getSystemClassLoader()));
112 WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
113 Object o = intHolder.get().newInstance();
114 Runtime.getRuntime().gc();
115 boolean isNull = loader.get() == null;
116 System.out.println("loader null " + isNull);
117 }
118
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700119 private static WeakReference<Class> setUpUnloadClass(Constructor constructor) throws Exception {
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700120 ClassLoader loader = (ClassLoader) constructor.newInstance(
121 DEX_FILE, ClassLoader.getSystemClassLoader());
122 Class intHolder = loader.loadClass("IntHolder");
123 Method getValue = intHolder.getDeclaredMethod("getValue");
124 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
125 // Make sure we don't accidentally preserve the value in the int holder, the class
126 // initializer should be re-run.
127 System.out.println((int) getValue.invoke(intHolder));
128 setValue.invoke(intHolder, 2);
129 System.out.println((int) getValue.invoke(intHolder));
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700130 waitForCompilation(intHolder);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700131 return new WeakReference(intHolder);
132 }
133
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700134 private static WeakReference<ClassLoader> setUpUnloadLoader(Constructor constructor,
135 boolean waitForCompilation)
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700136 throws Exception {
137 ClassLoader loader = (ClassLoader) constructor.newInstance(
138 DEX_FILE, ClassLoader.getSystemClassLoader());
139 Class intHolder = loader.loadClass("IntHolder");
140 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
141 setValue.invoke(intHolder, 2);
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700142 if (waitForCompilation) {
143 waitForCompilation(intHolder);
144 }
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700145 return new WeakReference(loader);
146 }
147
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700148 private static void waitForCompilation(Class intHolder) throws Exception {
149 // Load the native library so that we can call waitForCompilation.
150 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
151 loadLibrary.invoke(intHolder, nativeLibraryName);
152 // Wait for JIT compilation to finish since the async threads may prevent unloading.
153 Method waitForCompilation = intHolder.getDeclaredMethod("waitForCompilation");
154 waitForCompilation.invoke(intHolder);
155 }
156
Mathieu Chartier598302a2015-09-23 14:52:39 -0700157 private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor constructor)
158 throws Exception {
159 ClassLoader loader = (ClassLoader) constructor.newInstance(
160 DEX_FILE, ClassLoader.getSystemClassLoader());
161 Class intHolder = loader.loadClass("IntHolder");
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700162 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
163 loadLibrary.invoke(intHolder, nativeLibraryName);
Mathieu Chartier598302a2015-09-23 14:52:39 -0700164 return new WeakReference(loader);
165 }
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700166}