if (dex_cache_image_class_lookup_required_) {
MoveImageClassesToClassTable();
}
+ // TODO: why isn't this a ReaderMutexLock?
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
for (std::pair<const size_t, GcRoot<mirror::Class> >& it : class_table_) {
mirror::Class* c = it.second.Read();
}
}
-static bool GetClassesVisitor(mirror::Class* c, void* arg) {
+static bool GetClassesVisitorSet(mirror::Class* c, void* arg) {
std::set<mirror::Class*>* classes = reinterpret_cast<std::set<mirror::Class*>*>(arg);
classes->insert(c);
return true;
}
+struct GetClassesVisitorArrayArg {
+ Handle<mirror::ObjectArray<mirror::Class>>* classes;
+ int32_t index;
+ bool success;
+};
+
+static bool GetClassesVisitorArray(mirror::Class* c, void* varg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ GetClassesVisitorArrayArg* arg = reinterpret_cast<GetClassesVisitorArrayArg*>(varg);
+ if (arg->index < (*arg->classes)->GetLength()) {
+ (*arg->classes)->Set(arg->index, c);
+ arg->index++;
+ return true;
+ } else {
+ arg->success = false;
+ return false;
+ }
+}
+
void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) {
- std::set<mirror::Class*> classes;
- VisitClasses(GetClassesVisitor, &classes);
- for (mirror::Class* klass : classes) {
- if (!visitor(klass, arg)) {
- return;
+ // TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem
+ // is avoiding duplicates.
+ if (!kMovingClasses) {
+ std::set<mirror::Class*> classes;
+ VisitClasses(GetClassesVisitorSet, &classes);
+ for (mirror::Class* klass : classes) {
+ if (!visitor(klass, arg)) {
+ return;
+ }
+ }
+ } else {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ObjectArray<mirror::Class>> classes =
+ hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
+ GetClassesVisitorArrayArg local_arg;
+ local_arg.classes = &classes;
+ local_arg.success = false;
+ // We size the array assuming classes won't be added to the class table during the visit.
+ // If this assumption fails we iterate again.
+ while (!local_arg.success) {
+ size_t class_table_size;
+ {
+ ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ class_table_size = class_table_.size();
+ }
+ mirror::Class* class_type = mirror::Class::GetJavaLangClass();
+ mirror::Class* array_of_class = FindArrayClass(self, &class_type);
+ classes.Assign(
+ mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size));
+ CHECK(classes.Get() != nullptr); // OOME.
+ local_arg.index = 0;
+ local_arg.success = true;
+ VisitClasses(GetClassesVisitorArray, &local_arg);
+ }
+ for (int32_t i = 0; i < classes->GetLength(); ++i) {
+ // If the class table shrank during creation of the clases array we expect null elements. If
+ // the class table grew then the loop repeats. If classes are created after the loop has
+ // finished then we don't visit.
+ mirror::Class* klass = classes->Get(i);
+ if (klass != nullptr && !visitor(klass, arg)) {
+ return;
+ }
}
}
}
}
void VisitClasses(ClassVisitor* visitor, void* arg)
- LOCKS_EXCLUDED(dex_lock_)
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Less efficient variant of VisitClasses that doesn't hold the classlinker_classes_lock_
- // when calling the visitor.
+
+ // Less efficient variant of VisitClasses that copies the class_table_ into secondary storage
+ // so that it can visit individual classes without holding the doesn't hold the
+ // Locks::classlinker_classes_lock_. As the Locks::classlinker_classes_lock_ isn't held this code
+ // can race with insertion and deletion of classes while the visitor is being called.
void VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg)
- LOCKS_EXCLUDED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags)
ConstHandle<mirror::ArtMethod> prototype)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- std::vector<const DexFile*> boot_class_path_;
-
- mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::vector<size_t> new_dex_cache_roots_ GUARDED_BY(dex_lock_);;
- std::vector<GcRoot<mirror::DexCache>> dex_caches_ GUARDED_BY(dex_lock_);
- std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_);
-
-
- // multimap from a string hash code of a class descriptor to
- // mirror::Class* instances. Results should be compared for a matching
- // Class::descriptor_ and Class::class_loader_.
- typedef AllocationTrackingMultiMap<size_t, GcRoot<mirror::Class>, kAllocatorTagClassTable> Table;
- // This contains strong roots. To enable concurrent root scanning of
- // the class table, be careful to use a read barrier when accessing this.
- Table class_table_ GUARDED_BY(Locks::classlinker_classes_lock_);
- std::vector<std::pair<size_t, GcRoot<mirror::Class>>> new_class_roots_;
-
- // Do we need to search dex caches to find image classes?
- bool dex_cache_image_class_lookup_required_;
- // Number of times we've searched dex caches for a class. After a certain number of misses we move
- // the classes into the class_table_ to avoid dex cache based searches.
- Atomic<uint32_t> failed_dex_cache_class_lookups_;
-
mirror::Class* LookupClassFromTableLocked(const char* descriptor,
const mirror::ClassLoader* class_loader,
size_t hash)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void MoveImageClassesToClassTable() LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::Class* LookupClassFromImage(const char* descriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::vector<const DexFile*> boot_class_path_;
+
+ mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ std::vector<size_t> new_dex_cache_roots_ GUARDED_BY(dex_lock_);;
+ std::vector<GcRoot<mirror::DexCache>> dex_caches_ GUARDED_BY(dex_lock_);
+ std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_);
+
+
+ // multimap from a string hash code of a class descriptor to
+ // mirror::Class* instances. Results should be compared for a matching
+ // Class::descriptor_ and Class::class_loader_.
+ typedef AllocationTrackingMultiMap<size_t, GcRoot<mirror::Class>, kAllocatorTagClassTable> Table;
+ // This contains strong roots. To enable concurrent root scanning of
+ // the class table, be careful to use a read barrier when accessing this.
+ Table class_table_ GUARDED_BY(Locks::classlinker_classes_lock_);
+ std::vector<std::pair<size_t, GcRoot<mirror::Class>>> new_class_roots_;
+
+ // Do we need to search dex caches to find image classes?
+ bool dex_cache_image_class_lookup_required_;
+ // Number of times we've searched dex caches for a class. After a certain number of misses we move
+ // the classes into the class_table_ to avoid dex cache based searches.
+ Atomic<uint32_t> failed_dex_cache_class_lookups_;
+
// indexes into class_roots_.
// needs to be kept in sync with class_roots_descriptors_.
enum ClassRoot {