1 /*
2 * Copyright (C) 2012 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 <stdio.h>
18 #include <unistd.h>
19 #include <sys/mman.h>
20
21 #include "garbage_collector.h"
22
23 #include "android-base/stringprintf.h"
24
25 #include "base/dumpable.h"
26 #include "base/histogram-inl.h"
27 #include "base/logging.h" // For VLOG_IS_ON.
28 #include "base/mutex-inl.h"
29 #include "base/systrace.h"
30 #include "base/time_utils.h"
31 #include "base/utils.h"
32 #include "gc/accounting/heap_bitmap.h"
33 #include "gc/gc_pause_listener.h"
34 #include "gc/heap.h"
35 #include "gc/space/large_object_space.h"
36 #include "gc/space/space-inl.h"
37 #include "runtime.h"
38 #include "thread-current-inl.h"
39 #include "thread_list.h"
40
41 namespace art {
42 namespace gc {
43 namespace collector {
44
Iteration()45 Iteration::Iteration()
46 : duration_ns_(0), timings_("GC iteration timing logger", true, VLOG_IS_ON(heap)) {
47 Reset(kGcCauseBackground, false); // Reset to some place holder values.
48 }
49
Reset(GcCause gc_cause,bool clear_soft_references)50 void Iteration::Reset(GcCause gc_cause, bool clear_soft_references) {
51 timings_.Reset();
52 pause_times_.clear();
53 duration_ns_ = 0;
54 bytes_scanned_ = 0;
55 clear_soft_references_ = clear_soft_references;
56 gc_cause_ = gc_cause;
57 freed_ = ObjectBytePair();
58 freed_los_ = ObjectBytePair();
59 freed_bytes_revoke_ = 0;
60 }
61
GetEstimatedThroughput() const62 uint64_t Iteration::GetEstimatedThroughput() const {
63 // Add 1ms to prevent possible division by 0.
64 return (static_cast<uint64_t>(freed_.bytes) * 1000) / (NsToMs(GetDurationNs()) + 1);
65 }
66
GarbageCollector(Heap * heap,const std::string & name)67 GarbageCollector::GarbageCollector(Heap* heap, const std::string& name)
68 : heap_(heap),
69 name_(name),
70 pause_histogram_((name_ + " paused").c_str(), kPauseBucketSize, kPauseBucketCount),
71 rss_histogram_((name_ + " peak-rss").c_str(), kMemBucketSize, kMemBucketCount),
72 freed_bytes_histogram_((name_ + " freed-bytes").c_str(), kMemBucketSize, kMemBucketCount),
73 gc_time_histogram_(nullptr),
74 metrics_gc_count_(nullptr),
75 gc_throughput_histogram_(nullptr),
76 gc_tracing_throughput_hist_(nullptr),
77 gc_throughput_avg_(nullptr),
78 gc_tracing_throughput_avg_(nullptr),
79 cumulative_timings_(name),
80 pause_histogram_lock_("pause histogram lock", kDefaultMutexLevel, true),
81 is_transaction_active_(false),
82 are_metrics_initialized_(false) {
83 ResetCumulativeStatistics();
84 }
85
RegisterPause(uint64_t nano_length)86 void GarbageCollector::RegisterPause(uint64_t nano_length) {
87 GetCurrentIteration()->pause_times_.push_back(nano_length);
88 }
89
ResetCumulativeStatistics()90 void GarbageCollector::ResetCumulativeStatistics() {
91 cumulative_timings_.Reset();
92 total_thread_cpu_time_ns_ = 0u;
93 total_time_ns_ = 0u;
94 total_freed_objects_ = 0u;
95 total_freed_bytes_ = 0;
96 total_scanned_bytes_ = 0;
97 rss_histogram_.Reset();
98 freed_bytes_histogram_.Reset();
99 MutexLock mu(Thread::Current(), pause_histogram_lock_);
100 pause_histogram_.Reset();
101 }
102
ExtractRssFromMincore(std::list<std::pair<void *,void * >> * gc_ranges)103 uint64_t GarbageCollector::ExtractRssFromMincore(
104 std::list<std::pair<void*, void*>>* gc_ranges) {
105 uint64_t rss = 0;
106 if (gc_ranges->empty()) {
107 return 0;
108 }
109 // mincore() is linux-specific syscall.
110 #if defined(__linux__)
111 using range_t = std::pair<void*, void*>;
112 // Sort gc_ranges
113 gc_ranges->sort([](const range_t& a, const range_t& b) {
114 return std::less()(a.first, b.first);
115 });
116 // Merge gc_ranges. It's necessary because the kernel may merge contiguous
117 // regions if their properties match. This is sufficient as kernel doesn't
118 // merge those adjoining ranges which differ only in name.
119 size_t vec_len = 0;
120 for (auto it = gc_ranges->begin(); it != gc_ranges->end(); it++) {
121 auto next_it = it;
122 next_it++;
123 while (next_it != gc_ranges->end()) {
124 if (it->second == next_it->first) {
125 it->second = next_it->second;
126 next_it = gc_ranges->erase(next_it);
127 } else {
128 break;
129 }
130 }
131 size_t length = static_cast<uint8_t*>(it->second) - static_cast<uint8_t*>(it->first);
132 // Compute max length for vector allocation later.
133 vec_len = std::max(vec_len, length / kPageSize);
134 }
135 std::unique_ptr<unsigned char[]> vec(new unsigned char[vec_len]);
136 for (const auto it : *gc_ranges) {
137 size_t length = static_cast<uint8_t*>(it.second) - static_cast<uint8_t*>(it.first);
138 if (mincore(it.first, length, vec.get()) == 0) {
139 for (size_t i = 0; i < length / kPageSize; i++) {
140 // Least significant bit represents residency of a page. Other bits are
141 // reserved.
142 rss += vec[i] & 0x1;
143 }
144 } else {
145 LOG(WARNING) << "Call to mincore() on memory range [0x" << std::hex << it.first
146 << ", 0x" << it.second << std::dec << ") failed: " << strerror(errno);
147 }
148 }
149 rss *= kPageSize;
150 rss_histogram_.AddValue(rss / KB);
151 #endif
152 return rss;
153 }
154
Run(GcCause gc_cause,bool clear_soft_references)155 void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) {
156 ScopedTrace trace(android::base::StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName()));
157 Thread* self = Thread::Current();
158 Runtime* runtime = Runtime::Current();
159 uint64_t start_time = NanoTime();
160 uint64_t thread_cpu_start_time = ThreadCpuNanoTime();
161 GetHeap()->CalculatePreGcWeightedAllocatedBytes();
162 Iteration* current_iteration = GetCurrentIteration();
163 current_iteration->Reset(gc_cause, clear_soft_references);
164 // Note transaction mode is single-threaded and there's no asynchronous GC and this flag doesn't
165 // change in the middle of a GC.
166 is_transaction_active_ = runtime->IsActiveTransaction();
167 RunPhases(); // Run all the GC phases.
168 GetHeap()->CalculatePostGcWeightedAllocatedBytes();
169 // Add the current timings to the cumulative timings.
170 cumulative_timings_.AddLogger(*GetTimings());
171 // Update cumulative statistics with how many bytes the GC iteration freed.
172 total_freed_objects_ += current_iteration->GetFreedObjects() +
173 current_iteration->GetFreedLargeObjects();
174 total_scanned_bytes_ += current_iteration->GetScannedBytes();
175 int64_t freed_bytes = current_iteration->GetFreedBytes() +
176 current_iteration->GetFreedLargeObjectBytes();
177 total_freed_bytes_ += freed_bytes;
178 // Rounding negative freed bytes to 0 as we are not interested in such corner cases.
179 freed_bytes_histogram_.AddValue(std::max<int64_t>(freed_bytes / KB, 0));
180 uint64_t end_time = NanoTime();
181 uint64_t thread_cpu_end_time = ThreadCpuNanoTime();
182 total_thread_cpu_time_ns_ += thread_cpu_end_time - thread_cpu_start_time;
183 uint64_t duration_ns = end_time - start_time;
184 current_iteration->SetDurationNs(duration_ns);
185 if (Locks::mutator_lock_->IsExclusiveHeld(self)) {
186 // The entire GC was paused, clear the fake pauses which might be in the pause times and add
187 // the whole GC duration.
188 current_iteration->pause_times_.clear();
189 RegisterPause(duration_ns);
190 }
191 total_time_ns_ += duration_ns;
192 uint64_t total_pause_time = 0;
193 for (uint64_t pause_time : current_iteration->GetPauseTimes()) {
194 MutexLock mu(self, pause_histogram_lock_);
195 pause_histogram_.AdjustAndAddValue(pause_time);
196 total_pause_time += pause_time;
197 }
198 metrics::ArtMetrics* metrics = runtime->GetMetrics();
199 // Report STW pause time in microseconds.
200 metrics->WorldStopTimeDuringGCAvg()->Add(total_pause_time / 1'000);
201 // Report total collection time of all GCs put together.
202 metrics->TotalGcCollectionTime()->Add(NsToMs(duration_ns));
203 if (are_metrics_initialized_) {
204 metrics_gc_count_->Add(1);
205 // Report GC time in milliseconds.
206 gc_time_histogram_->Add(NsToMs(duration_ns));
207 // Throughput in bytes/s. Add 1us to prevent possible division by 0.
208 uint64_t throughput = (current_iteration->GetScannedBytes() * 1'000'000)
209 / (NsToUs(duration_ns) + 1);
210 // Report in MB/s.
211 throughput /= MB;
212 gc_tracing_throughput_hist_->Add(throughput);
213 gc_tracing_throughput_avg_->Add(throughput);
214
215 // Report GC throughput in MB/s.
216 throughput = current_iteration->GetEstimatedThroughput() / MB;
217 gc_throughput_histogram_->Add(throughput);
218 gc_throughput_avg_->Add(throughput);
219 }
220 is_transaction_active_ = false;
221 }
222
SwapBitmaps()223 void GarbageCollector::SwapBitmaps() {
224 TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
225 // Swap the live and mark bitmaps for each alloc space. This is needed since sweep re-swaps
226 // these bitmaps. The bitmap swapping is an optimization so that we do not need to clear the live
227 // bits of dead objects in the live bitmap.
228 const GcType gc_type = GetGcType();
229 for (const auto& space : GetHeap()->GetContinuousSpaces()) {
230 // We never allocate into zygote spaces.
231 if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect ||
232 (gc_type == kGcTypeFull &&
233 space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect)) {
234 if (space->GetLiveBitmap() != nullptr && !space->HasBoundBitmaps()) {
235 CHECK(space->IsContinuousMemMapAllocSpace());
236 space->AsContinuousMemMapAllocSpace()->SwapBitmaps();
237 }
238 }
239 }
240 for (const auto& disc_space : GetHeap()->GetDiscontinuousSpaces()) {
241 disc_space->AsLargeObjectSpace()->SwapBitmaps();
242 }
243 }
244
GetEstimatedMeanThroughput() const245 uint64_t GarbageCollector::GetEstimatedMeanThroughput() const {
246 // Add 1ms to prevent possible division by 0.
247 return (total_freed_bytes_ * 1000) / (NsToMs(GetCumulativeTimings().GetTotalNs()) + 1);
248 }
249
ResetMeasurements()250 void GarbageCollector::ResetMeasurements() {
251 {
252 MutexLock mu(Thread::Current(), pause_histogram_lock_);
253 pause_histogram_.Reset();
254 }
255 cumulative_timings_.Reset();
256 rss_histogram_.Reset();
257 freed_bytes_histogram_.Reset();
258 total_thread_cpu_time_ns_ = 0u;
259 total_time_ns_ = 0u;
260 total_freed_objects_ = 0u;
261 total_freed_bytes_ = 0;
262 total_scanned_bytes_ = 0u;
263 }
264
ScopedPause(GarbageCollector * collector,bool with_reporting)265 GarbageCollector::ScopedPause::ScopedPause(GarbageCollector* collector, bool with_reporting)
266 : start_time_(NanoTime()), collector_(collector), with_reporting_(with_reporting) {
267 Runtime* runtime = Runtime::Current();
268 runtime->GetThreadList()->SuspendAll(__FUNCTION__);
269 if (with_reporting) {
270 GcPauseListener* pause_listener = runtime->GetHeap()->GetGcPauseListener();
271 if (pause_listener != nullptr) {
272 pause_listener->StartPause();
273 }
274 }
275 }
276
~ScopedPause()277 GarbageCollector::ScopedPause::~ScopedPause() {
278 collector_->RegisterPause(NanoTime() - start_time_);
279 Runtime* runtime = Runtime::Current();
280 if (with_reporting_) {
281 GcPauseListener* pause_listener = runtime->GetHeap()->GetGcPauseListener();
282 if (pause_listener != nullptr) {
283 pause_listener->EndPause();
284 }
285 }
286 runtime->GetThreadList()->ResumeAll();
287 }
288
289 // Returns the current GC iteration and assocated info.
GetCurrentIteration()290 Iteration* GarbageCollector::GetCurrentIteration() {
291 return heap_->GetCurrentGcIteration();
292 }
GetCurrentIteration() const293 const Iteration* GarbageCollector::GetCurrentIteration() const {
294 return heap_->GetCurrentGcIteration();
295 }
296
RecordFree(const ObjectBytePair & freed)297 void GarbageCollector::RecordFree(const ObjectBytePair& freed) {
298 GetCurrentIteration()->freed_.Add(freed);
299 heap_->RecordFree(freed.objects, freed.bytes);
300 }
RecordFreeLOS(const ObjectBytePair & freed)301 void GarbageCollector::RecordFreeLOS(const ObjectBytePair& freed) {
302 GetCurrentIteration()->freed_los_.Add(freed);
303 heap_->RecordFree(freed.objects, freed.bytes);
304 }
305
GetTotalPausedTimeNs()306 uint64_t GarbageCollector::GetTotalPausedTimeNs() {
307 MutexLock mu(Thread::Current(), pause_histogram_lock_);
308 return pause_histogram_.AdjustedSum();
309 }
310
DumpPerformanceInfo(std::ostream & os)311 void GarbageCollector::DumpPerformanceInfo(std::ostream& os) {
312 const CumulativeLogger& logger = GetCumulativeTimings();
313 const size_t iterations = logger.GetIterations();
314 if (iterations == 0) {
315 return;
316 }
317 os << Dumpable<CumulativeLogger>(logger);
318 const uint64_t total_ns = logger.GetTotalNs();
319 const double seconds = NsToMs(total_ns) / 1000.0;
320 const uint64_t freed_bytes = GetTotalFreedBytes();
321 const uint64_t freed_objects = GetTotalFreedObjects();
322 const uint64_t scanned_bytes = GetTotalScannedBytes();
323 {
324 MutexLock mu(Thread::Current(), pause_histogram_lock_);
325 if (pause_histogram_.SampleSize() > 0) {
326 Histogram<uint64_t>::CumulativeData cumulative_data;
327 pause_histogram_.CreateHistogram(&cumulative_data);
328 pause_histogram_.PrintConfidenceIntervals(os, 0.99, cumulative_data);
329 }
330 }
331 #if defined(__linux__)
332 if (rss_histogram_.SampleSize() > 0) {
333 os << rss_histogram_.Name()
334 << ": Avg: " << PrettySize(rss_histogram_.Mean() * KB)
335 << " Max: " << PrettySize(rss_histogram_.Max() * KB)
336 << " Min: " << PrettySize(rss_histogram_.Min() * KB) << "\n";
337 os << "Peak-rss Histogram: ";
338 rss_histogram_.DumpBins(os);
339 os << "\n";
340 }
341 #endif
342 if (freed_bytes_histogram_.SampleSize() > 0) {
343 os << freed_bytes_histogram_.Name()
344 << ": Avg: " << PrettySize(freed_bytes_histogram_.Mean() * KB)
345 << " Max: " << PrettySize(freed_bytes_histogram_.Max() * KB)
346 << " Min: " << PrettySize(freed_bytes_histogram_.Min() * KB) << "\n";
347 os << "Freed-bytes histogram: ";
348 freed_bytes_histogram_.DumpBins(os);
349 os << "\n";
350 }
351 const double cpu_seconds = NsToMs(GetTotalCpuTime()) / 1000.0;
352 os << GetName() << " total time: " << PrettyDuration(total_ns)
353 << " mean time: " << PrettyDuration(total_ns / iterations) << "\n"
354 << GetName() << " freed: " << freed_objects
355 << " objects with total size " << PrettySize(freed_bytes) << "\n"
356 << GetName() << " throughput: " << freed_objects / seconds << "/s / "
357 << PrettySize(freed_bytes / seconds) << "/s"
358 << " per cpu-time: "
359 << static_cast<uint64_t>(freed_bytes / cpu_seconds) << "/s / "
360 << PrettySize(freed_bytes / cpu_seconds) << "/s\n"
361 << GetName() << " tracing throughput: "
362 << PrettySize(scanned_bytes / seconds) << "/s "
363 << " per cpu-time: "
364 << PrettySize(scanned_bytes / cpu_seconds) << "/s\n";
365 }
366
367 } // namespace collector
368 } // namespace gc
369 } // namespace art
370