Main.java revision 45a8522898702f6a725ae19d97bceedc8fc609a6
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
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";
23    static String nativeLibraryName;
24
25    public static void main(String[] args) throws Exception {
26        nativeLibraryName = args[0];
27        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 {
34            testUnloadClass(constructor);
35            testUnloadLoader(constructor);
36            // 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);
40            // Test JNI_OnLoad and JNI_OnUnload.
41            testLoadAndUnloadLibrary(constructor);
42            // 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) {
51            setUpUnloadLoader(constructor, false);
52            if (i % 10 == 0) {
53                Runtime.getRuntime().gc();
54            }
55        }
56    }
57
58    private static void testUnloadClass(Constructor constructor) throws Exception {
59        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());
67    }
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());
76    }
77
78    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
86    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
107    private static WeakReference<Class> setUpUnloadClass(Constructor constructor) throws Exception {
108        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));
118        waitForCompilation(intHolder);
119        return new WeakReference(intHolder);
120    }
121
122    private static WeakReference<ClassLoader> setUpUnloadLoader(Constructor constructor,
123                                                                boolean waitForCompilation)
124        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);
130        if (waitForCompilation) {
131            waitForCompilation(intHolder);
132        }
133        return new WeakReference(loader);
134    }
135
136    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
145    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");
150        Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
151        loadLibrary.invoke(intHolder, nativeLibraryName);
152        return new WeakReference(loader);
153    }
154}
155