Visit class roots from ClassLoader::VisitReferences
This causes the classes of a class loader to get marked when that
class loader gets marked instead of during class root visiting.
Bug: 22720414
Change-Id: If53f042aff1d9f7bf94ecbe6886601edda029b7d
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0886e32..f19263d 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -37,6 +37,7 @@
#include "base/unix_file/fd_file.h"
#include "base/value_object.h"
#include "class_linker-inl.h"
+#include "class_table-inl.h"
#include "compiler_callbacks.h"
#include "debugger.h"
#include "dex_file-inl.h"
@@ -582,6 +583,7 @@
// Setup the ClassLoader, verifying the object_size_.
class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;");
+ class_root->SetClassLoaderClass();
CHECK_EQ(class_root->GetObjectSize(), mirror::ClassLoader::InstanceSize());
SetClassRoot(kJavaLangClassLoader, class_root);
@@ -1273,15 +1275,10 @@
// Moving concurrent:
// 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(visitor, flags);
+ boot_class_table_.VisitRoots(buffered_visitor);
for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
// May be null for boot ClassLoader.
root.VisitRoot(visitor, RootInfo(kRootVMInternal));
- ClassTable* const class_table = root.Read()->GetClassTable();
- if (class_table != nullptr) {
- // May be null if we have no classes.
- class_table->VisitRoots(visitor, flags);
- }
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
@@ -2810,6 +2807,10 @@
}
VerifyObject(klass);
class_table->InsertWithHash(klass, hash);
+ if (class_loader != nullptr) {
+ // This is necessary because we need to have the card dirtied for remembered sets.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
+ }
if (log_new_class_table_roots_) {
new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
}
@@ -4375,6 +4376,11 @@
klass->SetFinalizable();
}
+ // Inherit class loader flag form super class.
+ if (super->IsClassLoaderClass()) {
+ klass->SetClassLoaderClass();
+ }
+
// Inherit reference flags (if any) from the superclass.
int reference_flags = (super->GetAccessFlags() & kAccReferenceFlagsMask);
if (reference_flags != 0) {
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
new file mode 100644
index 0000000..dc60a2c
--- /dev/null
+++ b/runtime/class_table-inl.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_CLASS_TABLE_INL_H_
+#define ART_RUNTIME_CLASS_TABLE_INL_H_
+
+#include "class_table.h"
+
+namespace art {
+
+template<class Visitor>
+void ClassTable::VisitRoots(Visitor& visitor) {
+ for (ClassSet& class_set : classes_) {
+ for (GcRoot<mirror::Class>& root : class_set) {
+ visitor.VisitRoot(root.AddressWithoutBarrier());
+ }
+ }
+}
+
+template<class Visitor>
+void ClassTable::VisitRoots(const Visitor& visitor) {
+ for (ClassSet& class_set : classes_) {
+ for (GcRoot<mirror::Class>& root : class_set) {
+ visitor.VisitRoot(root.AddressWithoutBarrier());
+ }
+ }
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_CLASS_TABLE_INL_H_
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index c245d4e..fc8e6c4 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -61,16 +61,6 @@
return existing;
}
-void ClassTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags ATTRIBUTE_UNUSED) {
- BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
- visitor, RootInfo(kRootStickyClass));
- for (ClassSet& class_set : classes_) {
- for (GcRoot<mirror::Class>& root : class_set) {
- buffered_visitor.VisitRoot(root);
- }
- }
-}
-
bool ClassTable::Visit(ClassVisitor* visitor) {
for (ClassSet& class_set : classes_) {
for (GcRoot<mirror::Class>& root : class_set) {
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 4182954..6b18d90 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -67,8 +67,15 @@
mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash)
REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
- void VisitRoots(RootVisitor* visitor, VisitRootFlags flags)
- REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ // NO_THREAD_SAFETY_ANALYSIS for object marking requiring heap bitmap lock.
+ template<class Visitor>
+ void VisitRoots(Visitor& visitor)
+ SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_)
+ NO_THREAD_SAFETY_ANALYSIS;
+ template<class Visitor>
+ void VisitRoots(const Visitor& visitor)
+ SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_)
+ NO_THREAD_SAFETY_ANALYSIS;
// Return false if the callback told us to exit.
bool Visit(ClassVisitor* visitor)
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index e2bcca2..5799b66 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -1276,7 +1276,7 @@
explicit MarkVisitor(MarkSweep* const mark_sweep) ALWAYS_INLINE : mark_sweep_(mark_sweep) {
}
- void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const
+ void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(Locks::heap_bitmap_lock_) {
if (kCheckLocks) {
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index e67ea3f..713797f 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -883,6 +883,7 @@
gc::EqAllocRecordTypesPtr<gc::AllocRecordStackTraceElement>> frames_;
std::unordered_map<const mirror::Object*, const gc::AllocRecordStackTrace*> allocation_records_;
+ friend class GcRootVisitor;
DISALLOW_COPY_AND_ASSIGN(Hprof);
};
@@ -1023,12 +1024,47 @@
++objects_in_segment_;
}
+// Use for visiting the GcRoots held live by ArtFields, ArtMethods, and ClassLoaders.
+class GcRootVisitor {
+ public:
+ explicit GcRootVisitor(Hprof* hprof) : hprof_(hprof) {}
+
+ void operator()(mirror::Object* obj ATTRIBUTE_UNUSED,
+ MemberOffset offset ATTRIBUTE_UNUSED,
+ bool is_static ATTRIBUTE_UNUSED) const {}
+
+ // Note that these don't have read barriers. Its OK however since the GC is guaranteed to not be
+ // running during the hprof dumping process.
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* obj = root->AsMirrorPtr();
+ // The two cases are either classes or dex cache arrays. If it is a dex cache array, then use
+ // VM internal. Otherwise the object is a declaring class of an ArtField or ArtMethod or a
+ // class from a ClassLoader.
+ hprof_->VisitRoot(obj, RootInfo(obj->IsClass() ? kRootStickyClass : kRootVMInternal));
+ }
+
+
+ private:
+ Hprof* const hprof_;
+};
+
void Hprof::DumpHeapObject(mirror::Object* obj) {
// Ignore classes that are retired.
if (obj->IsClass() && obj->AsClass()->IsRetired()) {
return;
}
+ GcRootVisitor visitor(this);
+ obj->VisitReferences<true>(visitor, VoidFunctor());
+
gc::Heap* const heap = Runtime::Current()->GetHeap();
const gc::space::ContinuousSpace* const space = heap->FindContinuousSpaceFromObject(obj, true);
HprofHeapId heap_type = HPROF_HEAP_APP;
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 513ab37..dc60a38 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -236,6 +236,15 @@
SetAccessFlags(flags | kAccClassIsStringClass);
}
+ ALWAYS_INLINE bool IsClassLoaderClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return (GetField32(AccessFlagsOffset()) & kAccClassIsClassLoaderClass) != 0;
+ }
+
+ ALWAYS_INLINE void SetClassLoaderClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+ uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
+ SetAccessFlags(flags | kAccClassIsClassLoaderClass);
+ }
+
// Returns true if the class is abstract.
ALWAYS_INLINE bool IsAbstract() SHARED_REQUIRES(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccAbstract) != 0;
diff --git a/runtime/mirror/class_loader-inl.h b/runtime/mirror/class_loader-inl.h
new file mode 100644
index 0000000..35f3664
--- /dev/null
+++ b/runtime/mirror/class_loader-inl.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_CLASS_LOADER_INL_H_
+#define ART_RUNTIME_MIRROR_CLASS_LOADER_INL_H_
+
+#include "class_loader.h"
+
+#include "base/mutex-inl.h"
+#include "class_table-inl.h"
+
+namespace art {
+namespace mirror {
+
+template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor>
+inline void ClassLoader::VisitReferences(mirror::Class* klass, const Visitor& visitor) {
+ // Visit instance fields first.
+ VisitInstanceFieldsReferences<kVisitClass>(klass, visitor);
+ // Visit classes loaded after.
+ ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ ClassTable* const class_table = GetClassTable();
+ if (class_table != nullptr) {
+ class_table->VisitRoots(visitor);
+ }
+}
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_CLASS_LOADER_INL_H_
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index 940aaa6..21c652a 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -26,6 +26,8 @@
namespace mirror {
+class Class;
+
// C++ mirror of java.lang.ClassLoader
class MANAGED ClassLoader : public Object {
public:
@@ -44,6 +46,12 @@
SetField64<false>(OFFSET_OF_OBJECT_MEMBER(ClassLoader, class_table_),
reinterpret_cast<uint64_t>(class_table));
}
+ // Visit instance fields of the class loader as well as its associated classes.
+ // Null class loader is handled by ClassLinker::VisitClassRoots.
+ template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor>
+ void VisitReferences(mirror::Class* klass, const Visitor& visitor)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Locks::classlinker_classes_lock_);
private:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index c5610b5..7b1660b 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -25,6 +25,7 @@
#include "array-inl.h"
#include "class.h"
#include "class_linker.h"
+#include "class_loader-inl.h"
#include "lock_word-inl.h"
#include "monitor.h"
#include "object_array-inl.h"
@@ -997,6 +998,18 @@
klass->VisitFieldsReferences<kVisitClass, true>(0, visitor);
}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline bool Object::IsClassLoader() {
+ return GetClass<kVerifyFlags>()->IsClassLoaderClass();
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline mirror::ClassLoader* Object::AsClassLoader() {
+ DCHECK(IsClassLoader<kVerifyFlags>());
+ return down_cast<mirror::ClassLoader*>(this);
+}
+
template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor,
typename JavaLangRefVisitor>
inline void Object::VisitReferences(const Visitor& visitor,
@@ -1010,6 +1023,9 @@
} else if (kVisitClass) {
visitor(this, ClassOffset(), false);
}
+ } else if (klass->IsClassLoaderClass()) {
+ mirror::ClassLoader* class_loader = AsClassLoader<kVerifyFlags>();
+ class_loader->VisitReferences<kVisitClass, kVerifyFlags>(klass, visitor);
} else {
DCHECK(!klass->IsVariableSize());
VisitInstanceFieldsReferences<kVisitClass>(klass, visitor);
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index eea9f37..4967a14 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -37,6 +37,7 @@
class Array;
class Class;
+class ClassLoader;
class FinalizerReference;
template<class T> class ObjectArray;
template<class T> class PrimitiveArray;
@@ -156,6 +157,11 @@
template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ObjectArray<T>* AsObjectArray() SHARED_REQUIRES(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ClassLoader* AsClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
+
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
bool IsArrayInstance() SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 8586dd1..8b363a6 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -55,6 +55,9 @@
// Special runtime-only flags.
// Note: if only kAccClassIsReference is set, we have a soft reference.
+// class is ClassLoader or one of its subclasses
+static constexpr uint32_t kAccClassIsClassLoaderClass = 0x10000000;
+
// class/ancestor overrides finalize()
static constexpr uint32_t kAccClassIsFinalizable = 0x80000000;
// class is a soft/weak/phantom ref