Home | History | Annotate | Download | only in gc
      1 /*
      2  * Copyright (C) 2013 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 
     17 #include "reference_queue.h"
     18 
     19 #include "accounting/card_table-inl.h"
     20 #include "collector/concurrent_copying.h"
     21 #include "heap.h"
     22 #include "mirror/class-inl.h"
     23 #include "mirror/object-inl.h"
     24 #include "mirror/reference-inl.h"
     25 
     26 namespace art {
     27 namespace gc {
     28 
     29 ReferenceQueue::ReferenceQueue(Mutex* lock) : lock_(lock), list_(nullptr) {
     30 }
     31 
     32 void ReferenceQueue::AtomicEnqueueIfNotEnqueued(Thread* self, mirror::Reference* ref) {
     33   DCHECK(ref != nullptr);
     34   MutexLock mu(self, *lock_);
     35   if (ref->IsUnprocessed()) {
     36     EnqueueReference(ref);
     37   }
     38 }
     39 
     40 void ReferenceQueue::EnqueueReference(mirror::Reference* ref) {
     41   DCHECK(ref != nullptr);
     42   CHECK(ref->IsUnprocessed());
     43   if (IsEmpty()) {
     44     // 1 element cyclic queue, ie: Reference ref = ..; ref.pendingNext = ref;
     45     list_ = ref;
     46   } else {
     47     mirror::Reference* head = list_->GetPendingNext();
     48     DCHECK(head != nullptr);
     49     ref->SetPendingNext(head);
     50   }
     51   // Add the reference in the middle to preserve the cycle.
     52   list_->SetPendingNext(ref);
     53 }
     54 
     55 mirror::Reference* ReferenceQueue::DequeuePendingReference() {
     56   DCHECK(!IsEmpty());
     57   mirror::Reference* ref = list_->GetPendingNext();
     58   DCHECK(ref != nullptr);
     59   // Note: the following code is thread-safe because it is only called from ProcessReferences which
     60   // is single threaded.
     61   if (list_ == ref) {
     62     list_ = nullptr;
     63   } else {
     64     mirror::Reference* next = ref->GetPendingNext();
     65     list_->SetPendingNext(next);
     66   }
     67   ref->SetPendingNext(nullptr);
     68   Heap* heap = Runtime::Current()->GetHeap();
     69   if (kUseBakerOrBrooksReadBarrier && heap->CurrentCollectorType() == kCollectorTypeCC &&
     70       heap->ConcurrentCopyingCollector()->IsActive()) {
     71     // Change the gray ptr we left in ConcurrentCopying::ProcessMarkStackRef() to black or white.
     72     // We check IsActive() above because we don't want to do this when the zygote compaction
     73     // collector (SemiSpace) is running.
     74     CHECK(ref != nullptr);
     75     collector::ConcurrentCopying* concurrent_copying = heap->ConcurrentCopyingCollector();
     76     const bool is_moving = concurrent_copying->RegionSpace()->IsInToSpace(ref);
     77     if (ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr()) {
     78       if (is_moving) {
     79         ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), ReadBarrier::WhitePtr());
     80         CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr());
     81       } else {
     82         ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), ReadBarrier::BlackPtr());
     83         CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr());
     84       }
     85     } else {
     86       // In ConcurrentCopying::ProcessMarkStackRef() we may leave a black or white Reference in the
     87       // queue and find it here, which is OK. Check that the color makes sense depending on whether
     88       // the Reference is moving or not and that the referent has been marked.
     89       if (is_moving) {
     90         CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr())
     91             << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer();
     92       } else {
     93         CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr())
     94             << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer();
     95       }
     96       mirror::Object* referent = ref->GetReferent<kWithoutReadBarrier>();
     97       // The referent could be null if it's cleared by a mutator (Reference.clear()).
     98       if (referent != nullptr) {
     99         CHECK(concurrent_copying->IsInToSpace(referent))
    100             << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer()
    101             << " referent=" << referent;
    102       }
    103     }
    104   }
    105   return ref;
    106 }
    107 
    108 void ReferenceQueue::Dump(std::ostream& os) const {
    109   mirror::Reference* cur = list_;
    110   os << "Reference starting at list_=" << list_ << "\n";
    111   if (cur == nullptr) {
    112     return;
    113   }
    114   do {
    115     mirror::Reference* pending_next = cur->GetPendingNext();
    116     os << "Reference= " << cur << " PendingNext=" << pending_next;
    117     if (cur->IsFinalizerReferenceInstance()) {
    118       os << " Zombie=" << cur->AsFinalizerReference()->GetZombie();
    119     }
    120     os << "\n";
    121     cur = pending_next;
    122   } while (cur != list_);
    123 }
    124 
    125 size_t ReferenceQueue::GetLength() const {
    126   size_t count = 0;
    127   mirror::Reference* cur = list_;
    128   if (cur != nullptr) {
    129     do {
    130       ++count;
    131       cur = cur->GetPendingNext();
    132     } while (cur != list_);
    133   }
    134   return count;
    135 }
    136 
    137 void ReferenceQueue::ClearWhiteReferences(ReferenceQueue* cleared_references,
    138                                           collector::GarbageCollector* collector) {
    139   while (!IsEmpty()) {
    140     mirror::Reference* ref = DequeuePendingReference();
    141     mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr();
    142     if (referent_addr->AsMirrorPtr() != nullptr &&
    143         !collector->IsMarkedHeapReference(referent_addr)) {
    144       // Referent is white, clear it.
    145       if (Runtime::Current()->IsActiveTransaction()) {
    146         ref->ClearReferent<true>();
    147       } else {
    148         ref->ClearReferent<false>();
    149       }
    150       cleared_references->EnqueueReference(ref);
    151     }
    152   }
    153 }
    154 
    155 void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue* cleared_references,
    156                                                 collector::GarbageCollector* collector) {
    157   while (!IsEmpty()) {
    158     mirror::FinalizerReference* ref = DequeuePendingReference()->AsFinalizerReference();
    159     mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr();
    160     if (referent_addr->AsMirrorPtr() != nullptr &&
    161         !collector->IsMarkedHeapReference(referent_addr)) {
    162       mirror::Object* forward_address = collector->MarkObject(referent_addr->AsMirrorPtr());
    163       // Move the updated referent to the zombie field.
    164       if (Runtime::Current()->IsActiveTransaction()) {
    165         ref->SetZombie<true>(forward_address);
    166         ref->ClearReferent<true>();
    167       } else {
    168         ref->SetZombie<false>(forward_address);
    169         ref->ClearReferent<false>();
    170       }
    171       cleared_references->EnqueueReference(ref);
    172     }
    173   }
    174 }
    175 
    176 void ReferenceQueue::ForwardSoftReferences(MarkObjectVisitor* visitor) {
    177   if (UNLIKELY(IsEmpty())) {
    178     return;
    179   }
    180   mirror::Reference* const head = list_;
    181   mirror::Reference* ref = head;
    182   do {
    183     mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr();
    184     if (referent_addr->AsMirrorPtr() != nullptr) {
    185       visitor->MarkHeapReference(referent_addr);
    186     }
    187     ref = ref->GetPendingNext();
    188   } while (LIKELY(ref != head));
    189 }
    190 
    191 void ReferenceQueue::UpdateRoots(IsMarkedVisitor* visitor) {
    192   if (list_ != nullptr) {
    193     list_ = down_cast<mirror::Reference*>(visitor->IsMarked(list_));
    194   }
    195 }
    196 
    197 }  // namespace gc
    198 }  // namespace art
    199