// Need to make sure to not copy ArtMethods without doing read barriers since the roots are
// marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy.
boot_class_table_.VisitRoots(buffered_visitor);
- // TODO: Avoid marking these to enable class unloading.
- JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
- for (const ClassLoaderData& data : class_loaders_) {
- mirror::Object* class_loader = vm->DecodeWeakGlobal(self, data.weak_root);
- // Don't need to update anything since the class loaders will be updated by SweepSystemWeaks.
- visitor->VisitRootIfNonNull(&class_loader, RootInfo(kRootVMInternal));
- }
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
ClassTable* const table = InsertClassTableForClassLoader(class_loader);
mirror::Class* existing = table->UpdateClass(descriptor, h_new_class.Get(),
ComputeModifiedUtf8Hash(descriptor));
+ if (class_loader != nullptr) {
+ // We updated the class in the class table, perform the write barrier so that the GC knows
+ // about the change.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
+ }
CHECK_EQ(existing, klass.Get());
if (kIsDebugBuild && class_loader == nullptr && dex_cache_image_class_lookup_required_) {
// Check a class loaded with the system class loader matches one in the image if the class
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Simple class that holds a static int for testing that class unloading works
+// and re-runs the class initializer.
+public class IntHolder {
+ private static int value = 1;
+
+ public static void setValue(int newValue) {
+ value = newValue;
+ }
+
+ public static int getValue() {
+ return value;
+ }
+
+ public static void runGC() {
+ Runtime.getRuntime().gc();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+public class Main {
+ static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/141-class-unload-ex.jar";
+
+ public static void main(String[] args) throws Exception {
+ Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+ if (pathClassLoader == null) {
+ throw new AssertionError("Couldn't find path class loader class");
+ }
+ Constructor constructor =
+ pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
+ try {
+ testUnloadClassAndLoader(constructor);
+ // Test that we don't unload if we have a Method keeping the class live.
+ testNoUnloadInvoke(constructor);
+ // Test that we don't unload if we have an instance.
+ testNoUnloadInstance(constructor);
+ // Stress test to make sure we dont leak memory.
+ stressTest(constructor);
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ }
+
+ private static void stressTest(Constructor constructor) throws Exception {
+ for (int i = 0; i <= 100; ++i) {
+ setUpUnloadLoader(constructor);
+ if (i % 10 == 0) {
+ Runtime.getRuntime().gc();
+ }
+ }
+ }
+
+ private static void testUnloadClassAndLoader(Constructor constructor) throws Exception {
+ WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor);
+ WeakReference<Class> klass = setUpUnloadClass(constructor);
+ // No strong refernces to class loader, should get unloaded.
+ Runtime.getRuntime().gc();
+ WeakReference<Class> klass2 = setUpUnloadClass(constructor);
+ Runtime.getRuntime().gc();
+ // If the weak reference is cleared, then it was unloaded.
+ System.out.println(klass.get());
+ System.out.println(klass2.get());
+ System.out.println(loader.get());
+ }
+
+ private static void testNoUnloadInvoke(Constructor constructor) throws Exception {
+ WeakReference<ClassLoader> loader =
+ new WeakReference((ClassLoader) constructor.newInstance(
+ DEX_FILE, ClassLoader.getSystemClassLoader()));
+ WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
+ intHolder.get().getDeclaredMethod("runGC").invoke(intHolder.get());
+ boolean isNull = loader.get() == null;
+ System.out.println("loader null " + isNull);
+ }
+
+ private static void testNoUnloadInstance(Constructor constructor) throws Exception {
+ WeakReference<ClassLoader> loader =
+ new WeakReference((ClassLoader) constructor.newInstance(
+ DEX_FILE, ClassLoader.getSystemClassLoader()));
+ WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
+ Object o = intHolder.get().newInstance();
+ Runtime.getRuntime().gc();
+ boolean isNull = loader.get() == null;
+ System.out.println("loader null " + isNull);
+ }
+
+ private static WeakReference<Class> setUpUnloadClass(Constructor constructor)
+ throws Exception {
+ ClassLoader loader = (ClassLoader) constructor.newInstance(
+ DEX_FILE, ClassLoader.getSystemClassLoader());
+ Class intHolder = loader.loadClass("IntHolder");
+ Method getValue = intHolder.getDeclaredMethod("getValue");
+ Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
+ // Make sure we don't accidentally preserve the value in the int holder, the class
+ // initializer should be re-run.
+ System.out.println((int) getValue.invoke(intHolder));
+ setValue.invoke(intHolder, 2);
+ System.out.println((int) getValue.invoke(intHolder));
+ return new WeakReference(intHolder);
+ }
+
+ private static WeakReference<ClassLoader> setUpUnloadLoader(Constructor constructor)
+ throws Exception {
+ ClassLoader loader = (ClassLoader) constructor.newInstance(
+ DEX_FILE, ClassLoader.getSystemClassLoader());
+ Class intHolder = loader.loadClass("IntHolder");
+ Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
+ setValue.invoke(intHolder, 2);
+ return new WeakReference(loader);
+ }
+
+}