• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 a Method keeping the class live.
41             testNoUnloadInvoke(constructor);
42             // Test that we don't unload if we have an instance.
43             testNoUnloadInstance(constructor);
44             // Test JNI_OnLoad and JNI_OnUnload.
45             testLoadAndUnloadLibrary(constructor);
46             // Test that stack traces keep the classes live.
47             testStackTrace(constructor);
48             // Stress test to make sure we dont leak memory.
49             stressTest(constructor);
50             // Test that the oat files are unloaded.
51             testOatFilesUnloaded(getPid());
52         } catch (Exception e) {
53             e.printStackTrace();
54         }
55     }
56 
testOatFilesUnloaded(int pid)57     private static void testOatFilesUnloaded(int pid) throws Exception {
58         BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps"));
59         String line;
60         int count = 0;
61         Runtime.getRuntime().gc();
62         System.runFinalization();
63         while ((line = reader.readLine()) != null) {
64             if (line.contains("@141-class-unload-ex.jar")) {
65                 System.out.println(line);
66                 ++count;
67             }
68         }
69         System.out.println("Number of loaded unload-ex maps " + count);
70     }
71 
stressTest(Constructor constructor)72     private static void stressTest(Constructor constructor) throws Exception {
73         for (int i = 0; i <= 100; ++i) {
74             setUpUnloadLoader(constructor, false);
75             if (i % 10 == 0) {
76                 Runtime.getRuntime().gc();
77             }
78         }
79     }
80 
testUnloadClass(Constructor constructor)81     private static void testUnloadClass(Constructor constructor) throws Exception {
82         WeakReference<Class> klass = setUpUnloadClass(constructor);
83         // No strong references to class loader, should get unloaded.
84         Runtime.getRuntime().gc();
85         WeakReference<Class> klass2 = setUpUnloadClass(constructor);
86         Runtime.getRuntime().gc();
87         // If the weak reference is cleared, then it was unloaded.
88         System.out.println(klass.get());
89         System.out.println(klass2.get());
90     }
91 
testUnloadLoader(Constructor constructor)92     private static void testUnloadLoader(Constructor constructor)
93         throws Exception {
94       WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true);
95       // No strong references to class loader, should get unloaded.
96       Runtime.getRuntime().gc();
97       // If the weak reference is cleared, then it was unloaded.
98       System.out.println(loader.get());
99     }
100 
testStackTrace(Constructor constructor)101     private static void testStackTrace(Constructor constructor) throws Exception {
102         WeakReference<Class> klass = setUpUnloadClass(constructor);
103         Method stackTraceMethod = klass.get().getDeclaredMethod("generateStackTrace");
104         Throwable throwable = (Throwable) stackTraceMethod.invoke(klass.get());
105         stackTraceMethod = null;
106         Runtime.getRuntime().gc();
107         boolean isNull = klass.get() == null;
108         System.out.println("class null " + isNull + " " + throwable.getMessage());
109     }
110 
testLoadAndUnloadLibrary(Constructor constructor)111     private static void testLoadAndUnloadLibrary(Constructor constructor) throws Exception {
112         WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor);
113         // No strong references to class loader, should get unloaded.
114         Runtime.getRuntime().gc();
115         // If the weak reference is cleared, then it was unloaded.
116         System.out.println(loader.get());
117     }
118 
testNoUnloadInvoke(Constructor constructor)119     private static void testNoUnloadInvoke(Constructor constructor) throws Exception {
120         WeakReference<ClassLoader> loader =
121             new WeakReference((ClassLoader) constructor.newInstance(
122                 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()));
123         WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
124         intHolder.get().getDeclaredMethod("runGC").invoke(intHolder.get());
125         boolean isNull = loader.get() == null;
126         System.out.println("loader null " + isNull);
127     }
128 
testNoUnloadInstance(Constructor constructor)129     private static void testNoUnloadInstance(Constructor constructor) throws Exception {
130         WeakReference<ClassLoader> loader =
131             new WeakReference((ClassLoader) constructor.newInstance(
132                 DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()));
133         WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
134         Object o = intHolder.get().newInstance();
135         Runtime.getRuntime().gc();
136         boolean isNull = loader.get() == null;
137         System.out.println("loader null " + isNull);
138     }
139 
setUpUnloadClass(Constructor constructor)140     private static WeakReference<Class> setUpUnloadClass(Constructor constructor) throws Exception {
141         ClassLoader loader = (ClassLoader) constructor.newInstance(
142             DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
143         Class intHolder = loader.loadClass("IntHolder");
144         Method getValue = intHolder.getDeclaredMethod("getValue");
145         Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
146         // Make sure we don't accidentally preserve the value in the int holder, the class
147         // initializer should be re-run.
148         System.out.println((int) getValue.invoke(intHolder));
149         setValue.invoke(intHolder, 2);
150         System.out.println((int) getValue.invoke(intHolder));
151         waitForCompilation(intHolder);
152         return new WeakReference(intHolder);
153     }
154 
setUpUnloadLoader(Constructor constructor, boolean waitForCompilation)155     private static WeakReference<ClassLoader> setUpUnloadLoader(Constructor constructor,
156                                                                 boolean waitForCompilation)
157         throws Exception {
158         ClassLoader loader = (ClassLoader) constructor.newInstance(
159             DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
160         Class intHolder = loader.loadClass("IntHolder");
161         Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
162         setValue.invoke(intHolder, 2);
163         if (waitForCompilation) {
164             waitForCompilation(intHolder);
165         }
166         return new WeakReference(loader);
167     }
168 
waitForCompilation(Class intHolder)169     private static void waitForCompilation(Class intHolder) throws Exception {
170       // Load the native library so that we can call waitForCompilation.
171       Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
172       loadLibrary.invoke(intHolder, nativeLibraryName);
173       // Wait for JIT compilation to finish since the async threads may prevent unloading.
174       Method waitForCompilation = intHolder.getDeclaredMethod("waitForCompilation");
175       waitForCompilation.invoke(intHolder);
176     }
177 
setUpLoadLibrary(Constructor constructor)178     private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor constructor)
179         throws Exception {
180         ClassLoader loader = (ClassLoader) constructor.newInstance(
181             DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
182         Class intHolder = loader.loadClass("IntHolder");
183         Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
184         loadLibrary.invoke(intHolder, nativeLibraryName);
185         waitForCompilation(intHolder);
186         return new WeakReference(loader);
187     }
188 
getPid()189     private static int getPid() throws Exception {
190       return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
191     }
192 }
193