blob: 105a2b981c03ad7fc738c78e13d43df9412e7c1f [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 Chartier05aa4d32015-09-19 12:44:38 -070042 // Stress test to make sure we dont leak memory.
43 stressTest(constructor);
44 } catch (Exception e) {
45 System.out.println(e);
46 }
47 }
48
49 private static void stressTest(Constructor constructor) throws Exception {
50 for (int i = 0; i <= 100; ++i) {
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070051 setUpUnloadLoader(constructor, false);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070052 if (i % 10 == 0) {
53 Runtime.getRuntime().gc();
54 }
55 }
56 }
57
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070058 private static void testUnloadClass(Constructor constructor) throws Exception {
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070059 WeakReference<Class> klass = setUpUnloadClass(constructor);
60 // No strong refernces to class loader, should get unloaded.
61 Runtime.getRuntime().gc();
62 WeakReference<Class> klass2 = setUpUnloadClass(constructor);
63 Runtime.getRuntime().gc();
64 // If the weak reference is cleared, then it was unloaded.
65 System.out.println(klass.get());
66 System.out.println(klass2.get());
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -070067 }
68
69 private static void testUnloadLoader(Constructor constructor)
70 throws Exception {
71 WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true);
72 // No strong refernces to class loader, should get unloaded.
73 Runtime.getRuntime().gc();
74 // If the weak reference is cleared, then it was unloaded.
75 System.out.println(loader.get());
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070076 }
77
Mathieu Chartier598302a2015-09-23 14:52:39 -070078 private static void testLoadAndUnloadLibrary(Constructor constructor) throws Exception {
79 WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor);
80 // No strong refernces to class loader, should get unloaded.
81 Runtime.getRuntime().gc();
82 // If the weak reference is cleared, then it was unloaded.
83 System.out.println(loader.get());
84 }
85
Mathieu Chartier05aa4d32015-09-19 12:44:38 -070086 private static void testNoUnloadInvoke(Constructor constructor) throws Exception {
87 WeakReference<ClassLoader> loader =
88 new WeakReference((ClassLoader) constructor.newInstance(
89 DEX_FILE, ClassLoader.getSystemClassLoader()));
90 WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
91 intHolder.get().getDeclaredMethod("runGC").invoke(intHolder.get());
92 boolean isNull = loader.get() == null;
93 System.out.println("loader null " + isNull);
94 }
95
96 private static void testNoUnloadInstance(Constructor constructor) throws Exception {
97 WeakReference<ClassLoader> loader =
98 new WeakReference((ClassLoader) constructor.newInstance(
99 DEX_FILE, ClassLoader.getSystemClassLoader()));
100 WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
101 Object o = intHolder.get().newInstance();
102 Runtime.getRuntime().gc();
103 boolean isNull = loader.get() == null;
104 System.out.println("loader null " + isNull);
105 }
106
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700107 private static WeakReference<Class> setUpUnloadClass(Constructor constructor) throws Exception {
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700108 ClassLoader loader = (ClassLoader) constructor.newInstance(
109 DEX_FILE, ClassLoader.getSystemClassLoader());
110 Class intHolder = loader.loadClass("IntHolder");
111 Method getValue = intHolder.getDeclaredMethod("getValue");
112 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
113 // Make sure we don't accidentally preserve the value in the int holder, the class
114 // initializer should be re-run.
115 System.out.println((int) getValue.invoke(intHolder));
116 setValue.invoke(intHolder, 2);
117 System.out.println((int) getValue.invoke(intHolder));
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700118 waitForCompilation(intHolder);
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700119 return new WeakReference(intHolder);
120 }
121
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700122 private static WeakReference<ClassLoader> setUpUnloadLoader(Constructor constructor,
123 boolean waitForCompilation)
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700124 throws Exception {
125 ClassLoader loader = (ClassLoader) constructor.newInstance(
126 DEX_FILE, ClassLoader.getSystemClassLoader());
127 Class intHolder = loader.loadClass("IntHolder");
128 Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
129 setValue.invoke(intHolder, 2);
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700130 if (waitForCompilation) {
131 waitForCompilation(intHolder);
132 }
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700133 return new WeakReference(loader);
134 }
135
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700136 private static void waitForCompilation(Class intHolder) throws Exception {
137 // Load the native library so that we can call waitForCompilation.
138 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
139 loadLibrary.invoke(intHolder, nativeLibraryName);
140 // Wait for JIT compilation to finish since the async threads may prevent unloading.
141 Method waitForCompilation = intHolder.getDeclaredMethod("waitForCompilation");
142 waitForCompilation.invoke(intHolder);
143 }
144
Mathieu Chartier598302a2015-09-23 14:52:39 -0700145 private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor constructor)
146 throws Exception {
147 ClassLoader loader = (ClassLoader) constructor.newInstance(
148 DEX_FILE, ClassLoader.getSystemClassLoader());
149 Class intHolder = loader.loadClass("IntHolder");
Mathieu Chartiera50f9cf2015-09-25 11:34:45 -0700150 Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
151 loadLibrary.invoke(intHolder, nativeLibraryName);
Mathieu Chartier598302a2015-09-23 14:52:39 -0700152 return new WeakReference(loader);
153 }
Mathieu Chartier05aa4d32015-09-19 12:44:38 -0700154}