namespace art HIDDEN { namespace gc { namespace collector {
namespace {
// Report a GC metric via the ATrace interface. void TraceGCMetric(constchar* name, int64_t value) { // ART's interface with systrace (through libartpalette) only supports // reporting 32-bit (signed) integer values at the moment. Upon // underflows/overflows, clamp metric values at `int32_t` min/max limits and // report these events via a corresponding underflow/overflow counter; also // log a warning about the first underflow/overflow occurrence. // // TODO(b/300015145): Consider extending libarpalette to allow reporting this // value as a 64-bit (signed) integer (instead of a 32-bit (signed) integer). // Note that this is likely unnecessary at the moment (November 2023) for any // size-related GC metric, given the maximum theoretical size of a managed // heap (4 GiB). if (UNLIKELY(value < std::numeric_limits<int32_t>::min())) {
ATraceIntegerValue(name, std::numeric_limits<int32_t>::min());
std::string underflow_counter_name = std::string(name) + " int32_t underflow";
ATraceIntegerValue(underflow_counter_name.c_str(), 1); staticbool int32_underflow_reported = false; if (!int32_underflow_reported) {
LOG(WARNING) << "GC Metric \"" << name << "\" with value " << value
<< " causing a 32-bit integer underflow";
int32_underflow_reported = true;
} return;
} if (UNLIKELY(value > std::numeric_limits<int32_t>::max())) {
ATraceIntegerValue(name, std::numeric_limits<int32_t>::max());
std::string overflow_counter_name = std::string(name) + " int32_t overflow";
ATraceIntegerValue(overflow_counter_name.c_str(), 1); staticbool int32_overflow_reported = false; if (!int32_overflow_reported) {
LOG(WARNING) << "GC Metric \"" << name << "\" with value " << value
<< " causing a 32-bit integer overflow";
int32_overflow_reported = true;
} return;
}
ATraceIntegerValue(name, value);
}
} // namespace
Iteration::Iteration()
: duration_ns_(0),
thread_cpu_time_ns_(0),
timings_("GC iteration timing logger", true, VLOG_IS_ON(heap)) {
Reset(kGcCauseBackground, false); // Reset to some place holder values.
}
// Report some metrics via the ATrace interface, to surface them in Perfetto.
TraceGCMetric("freed_normal_object_bytes", current_iteration->GetFreedBytes());
TraceGCMetric("freed_large_object_bytes", current_iteration->GetFreedLargeObjectBytes());
TraceGCMetric("freed_bytes", freed_bytes);
is_transaction_active_ = false;
}
void GarbageCollector::SwapBitmaps() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // Swap the live and mark bitmaps for each alloc space. This is needed since sweep re-swaps // these bitmaps. The bitmap swapping is an optimization so that we do not need to clear the live // bits of dead objects in the live bitmap. const GcType gc_type = GetGcType(); for (constauto& space : GetHeap()->GetContinuousSpaces()) { // We never allocate into zygote spaces. if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect ||
(gc_type == kGcTypeFull &&
space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect)) { if (space->GetLiveBitmap() != nullptr && !space->HasBoundBitmaps()) {
CHECK(space->IsContinuousMemMapAllocSpace());
space->AsContinuousMemMapAllocSpace()->SwapBitmaps();
}
}
} for (constauto& disc_space : GetHeap()->GetDiscontinuousSpaces()) {
disc_space->AsLargeObjectSpace()->SwapBitmaps();
}
}
void GarbageCollector::SweepArray(accounting::ObjectStack* allocations, bool swap_bitmaps,
std::vector<space::ContinuousSpace*>* sweep_spaces) {
Thread* self = Thread::Current(); static constexpr size_t kSweepArrayChunkFreeSize = 1024;
mirror::Object** chunk_free_buffer = new mirror::Object*[kSweepArrayChunkFreeSize];
size_t chunk_free_pos = 0;
ObjectBytePair freed;
ObjectBytePair freed_los; // How many objects are left in the array, modified after each space is swept.
StackReference<mirror::Object>* objects = allocations->Begin();
size_t count = allocations->Size(); // Start by sweeping the continuous spaces. for (space::ContinuousSpace* space : *sweep_spaces) {
space::AllocSpace* alloc_space = space->AsAllocSpace();
accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap(); if (swap_bitmaps) {
std::swap(live_bitmap, mark_bitmap);
}
StackReference<mirror::Object>* out = objects; for (size_t i = 0; i < count; ++i) {
mirror::Object* const obj = objects[i].AsMirrorPtr(); if (obj == nullptr) { continue;
} if (space->HasAddress(obj)) { // This object is in the space, remove it from the array and add it to the sweep buffer // if needed. if (!mark_bitmap->Test(obj)) { // Handle the case where buffer allocation failed. if (LIKELY(chunk_free_buffer != nullptr)) { if (chunk_free_pos >= kSweepArrayChunkFreeSize) {
TimingLogger::ScopedTiming t2("FreeList", GetTimings());
freed.objects += chunk_free_pos;
freed.bytes += alloc_space->FreeList(self, chunk_free_pos, chunk_free_buffer);
chunk_free_pos = 0;
}
chunk_free_buffer[chunk_free_pos++] = obj;
} else {
freed.objects++;
freed.bytes += alloc_space->Free(self, obj);
}
}
} else {
(out++)->Assign(obj);
}
} if (chunk_free_pos > 0) {
TimingLogger::ScopedTiming t2("FreeList", GetTimings());
freed.objects += chunk_free_pos;
freed.bytes += alloc_space->FreeList(self, chunk_free_pos, chunk_free_buffer);
chunk_free_pos = 0;
} // All of the references which space contained are no longer in the allocation stack, update // the count.
count = out - objects;
} if (chunk_free_buffer != nullptr) { delete[] chunk_free_buffer;
} // Handle the large object space.
space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); if (large_object_space != nullptr) {
accounting::LargeObjectBitmap* large_live_objects = large_object_space->GetLiveBitmap();
accounting::LargeObjectBitmap* large_mark_objects = large_object_space->GetMarkBitmap(); if (swap_bitmaps) {
std::swap(large_live_objects, large_mark_objects);
} for (size_t i = 0; i < count; ++i) {
mirror::Object* const obj = objects[i].AsMirrorPtr(); // Handle large objects. if (kUseThreadLocalAllocationStack && obj == nullptr) { continue;
} if (!large_mark_objects->Test(obj)) {
++freed_los.objects;
freed_los.bytes += large_object_space->Free(self, obj);
}
}
}
{
TimingLogger::ScopedTiming t2("RecordFree", GetTimings());
RecordFree(freed);
RecordFreeLOS(freed_los);
t2.NewTiming("ResetStack");
allocations->Reset();
}
}
// Returns the current GC iteration and assocated info.
Iteration* GarbageCollector::GetCurrentIteration() { return heap_->GetCurrentGcIteration();
} const Iteration* GarbageCollector::GetCurrentIteration() const { return heap_->GetCurrentGcIteration();
}
bool GarbageCollector::ShouldEagerlyReleaseMemoryToOS() const { // We have seen old kernels and custom kernel features misbehave in the // presence of too much usage of MADV_FREE. So only release memory eagerly // on platforms we know do not have the bug. staticconstbool gEnableLazyRelease = !kIsTargetBuild || IsKernelVersionAtLeast(6, 0); if (!gEnableLazyRelease) { returntrue;
}
Runtime* runtime = Runtime::Current(); // Zygote isn't a memory heavy process, we should always instantly release memory to the OS. if (runtime->IsZygote()) { returntrue;
} if (GetCurrentIteration()->GetGcCause() == kGcCauseExplicit &&
!runtime->IsEagerlyReleaseExplicitGcDisabled()) { // Our behavior with explicit GCs is to always release any available memory. returntrue;
} // Keep on the memory if the app is in foreground. If it is in background or // goes into the background (see invocation with cause kGcCauseCollectorTransition), // release the memory. return !runtime->InJankPerceptibleProcessState();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.