170 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			170 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2013 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_GC_HEAP_VISIT_OBJECTS_INL_H_
 | |
| #define ART_RUNTIME_GC_HEAP_VISIT_OBJECTS_INL_H_
 | |
| 
 | |
| #include "heap.h"
 | |
| 
 | |
| #include "base/mutex-inl.h"
 | |
| #include "gc/accounting/heap_bitmap-inl.h"
 | |
| #include "gc/space/bump_pointer_space-walk-inl.h"
 | |
| #include "gc/space/region_space-inl.h"
 | |
| #include "mirror/object-inl.h"
 | |
| #include "obj_ptr-inl.h"
 | |
| #include "scoped_thread_state_change-inl.h"
 | |
| #include "thread-current-inl.h"
 | |
| #include "thread_list.h"
 | |
| 
 | |
| namespace art {
 | |
| namespace gc {
 | |
| 
 | |
| // Visit objects when threads aren't suspended. If concurrent moving
 | |
| // GC, disable moving GC and suspend threads and then visit objects.
 | |
| template <typename Visitor>
 | |
| inline void Heap::VisitObjects(Visitor&& visitor) {
 | |
|   Thread* self = Thread::Current();
 | |
|   Locks::mutator_lock_->AssertSharedHeld(self);
 | |
|   DCHECK(!Locks::mutator_lock_->IsExclusiveHeld(self)) << "Call VisitObjectsPaused() instead";
 | |
|   if (IsGcConcurrentAndMoving()) {
 | |
|     // Concurrent moving GC. Just suspending threads isn't sufficient
 | |
|     // because a collection isn't one big pause and we could suspend
 | |
|     // threads in the middle (between phases) of a concurrent moving
 | |
|     // collection where it's not easily known which objects are alive
 | |
|     // (both the region space and the non-moving space) or which
 | |
|     // copies of objects to visit, and the to-space invariant could be
 | |
|     // easily broken. Visit objects while GC isn't running by using
 | |
|     // IncrementDisableMovingGC() and threads are suspended.
 | |
|     IncrementDisableMovingGC(self);
 | |
|     {
 | |
|       ScopedThreadSuspension sts(self, ThreadState::kWaitingForVisitObjects);
 | |
|       ScopedSuspendAll ssa(__FUNCTION__);
 | |
|       VisitObjectsInternalRegionSpace(visitor);
 | |
|       VisitObjectsInternal(visitor);
 | |
|     }
 | |
|     DecrementDisableMovingGC(self);
 | |
|   } else {
 | |
|     // Since concurrent moving GC has thread suspension, also poison ObjPtr the normal case to
 | |
|     // catch bugs.
 | |
|     self->PoisonObjectPointers();
 | |
|     // GCs can move objects, so don't allow this.
 | |
|     ScopedAssertNoThreadSuspension ants("Visiting objects");
 | |
|     DCHECK(region_space_ == nullptr);
 | |
|     VisitObjectsInternal(visitor);
 | |
|     self->PoisonObjectPointers();
 | |
|   }
 | |
| }
 | |
| 
 | |
| template <typename Visitor>
 | |
| inline void Heap::VisitObjectsPaused(Visitor&& visitor) {
 | |
|   Thread* self = Thread::Current();
 | |
|   Locks::mutator_lock_->AssertExclusiveHeld(self);
 | |
|   VisitObjectsInternalRegionSpace(visitor);
 | |
|   VisitObjectsInternal(visitor);
 | |
| }
 | |
| 
 | |
| // Visit objects in the region spaces.
 | |
| template <typename Visitor>
 | |
| inline void Heap::VisitObjectsInternalRegionSpace(Visitor&& visitor) {
 | |
|   Thread* self = Thread::Current();
 | |
|   Locks::mutator_lock_->AssertExclusiveHeld(self);
 | |
|   if (region_space_ != nullptr) {
 | |
|     DCHECK(IsGcConcurrentAndMoving());
 | |
|     if (!zygote_creation_lock_.IsExclusiveHeld(self)) {
 | |
|       // Exclude the pre-zygote fork time where the semi-space collector
 | |
|       // calls VerifyHeapReferences() as part of the zygote compaction
 | |
|       // which then would call here without the moving GC disabled,
 | |
|       // which is fine.
 | |
|       bool is_thread_running_gc = false;
 | |
|       if (kIsDebugBuild) {
 | |
|         MutexLock mu(self, *gc_complete_lock_);
 | |
|         is_thread_running_gc = self == thread_running_gc_;
 | |
|       }
 | |
|       // If we are not the thread running the GC on in a GC exclusive region, then moving GC
 | |
|       // must be disabled.
 | |
|       DCHECK(is_thread_running_gc || IsMovingGCDisabled(self));
 | |
|     }
 | |
|     region_space_->Walk(visitor);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Visit objects in the other spaces.
 | |
| template <typename Visitor>
 | |
| inline void Heap::VisitObjectsInternal(Visitor&& visitor) {
 | |
|   if (bump_pointer_space_ != nullptr) {
 | |
|     // Visit objects in bump pointer space.
 | |
|     bump_pointer_space_->Walk(visitor);
 | |
|   }
 | |
|   // TODO: Switch to standard begin and end to use ranged a based loop.
 | |
|   for (auto* it = allocation_stack_->Begin(), *end = allocation_stack_->End(); it < end; ++it) {
 | |
|     mirror::Object* const obj = it->AsMirrorPtr();
 | |
| 
 | |
|     mirror::Class* kls = nullptr;
 | |
|     if (obj != nullptr && (kls = obj->GetClass()) != nullptr) {
 | |
|       // Below invariant is safe regardless of what space the Object is in.
 | |
|       // For speed reasons, only perform it when Rosalloc could possibly be used.
 | |
|       // (Disabled for read barriers because it never uses Rosalloc).
 | |
|       // (See the DCHECK in RosAllocSpace constructor).
 | |
|       if (!kUseReadBarrier) {
 | |
|         // Rosalloc has a race in allocation. Objects can be written into the allocation
 | |
|         // stack before their header writes are visible to this thread.
 | |
|         // See b/28790624 for more details.
 | |
|         //
 | |
|         // obj.class will either be pointing to a valid Class*, or it will point
 | |
|         // to a rosalloc free buffer.
 | |
|         //
 | |
|         // If it's pointing to a valid Class* then that Class's Class will be the
 | |
|         // ClassClass (whose Class is itself).
 | |
|         //
 | |
|         // A rosalloc free buffer will point to another rosalloc free buffer
 | |
|         // (or to null), and never to itself.
 | |
|         //
 | |
|         // Either way dereferencing while its not-null is safe because it will
 | |
|         // always point to another valid pointer or to null.
 | |
|         mirror::Class* klsClass = kls->GetClass();
 | |
| 
 | |
|         if (klsClass == nullptr) {
 | |
|           continue;
 | |
|         } else if (klsClass->GetClass() != klsClass) {
 | |
|           continue;
 | |
|         }
 | |
|       } else {
 | |
|         // Ensure the invariant is not broken for non-rosalloc cases.
 | |
|         DCHECK(Heap::rosalloc_space_ == nullptr)
 | |
|             << "unexpected rosalloc with read barriers";
 | |
|         DCHECK(kls->GetClass() != nullptr)
 | |
|             << "invalid object: class does not have a class";
 | |
|         DCHECK_EQ(kls->GetClass()->GetClass(), kls->GetClass())
 | |
|             << "invalid object: class's class is not ClassClass";
 | |
|       }
 | |
| 
 | |
|       // Avoid the race condition caused by the object not yet being written into the allocation
 | |
|       // stack or the class not yet being written in the object. Or, if
 | |
|       // kUseThreadLocalAllocationStack, there can be nulls on the allocation stack.
 | |
|       visitor(obj);
 | |
|     }
 | |
|   }
 | |
|   {
 | |
|     ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
 | |
|     GetLiveBitmap()->Visit<Visitor>(visitor);
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace gc
 | |
| }  // namespace art
 | |
| 
 | |
| #endif  // ART_RUNTIME_GC_HEAP_VISIT_OBJECTS_INL_H_
 |