Skip to content

Commit cac15bc

Browse files
hboehmkdrag0n
authored andcommitted
Reland "Trigger fewer GCs during startup""
This reverts commit da90ab4. PS1 is identical to aosp/1653767 : Instead of explicitly triggering a GC after two seconds, gradually reduce the GC triggering threshold. In particular, a small process that almost immediately goes into the background should GC only as part of the transition to background. Ensure that the first collection is a full (technically "partial", non-sticky) gc, that tries to collect everything but zygote space. There should be very few allocated objects except in zygote space. Clarify the concurrency rules for accessing concurrent_start_bytes_ in the process. PS2: Adds code to explicitly trigger a GC if none has been triggered in the first 30 seconds or so. For AOSP, this happens in seven processes. No longer condition any of this on the CC collector. I don't see why that should matter. Trigger the low-allocation GC above even in low memory mode. I think we want to especially do it in that case. We were previously not doing that, probably it was tied to increasing the heap size. Test: Build and boot AOSP. Test: Manual log inspection with extra logging. Bug: 181351667 Bug: 197780496 Change-Id: I822224bef3e97c2ab1f803eafe97bcdd21b9cd4e
1 parent 8d44c3d commit cac15bc

File tree

2 files changed

+137
-27
lines changed

2 files changed

+137
-27
lines changed

runtime/gc/heap.cc

Lines changed: 109 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#include <malloc.h> // For mallinfo()
2323
#endif
2424
#include <memory>
25+
#include <random>
26+
#include <unistd.h>
27+
#include <sys/types.h>
2528
#include <vector>
2629

2730
#include "android-base/stringprintf.h"
@@ -316,6 +319,7 @@ Heap::Heap(size_t initial_size,
316319
next_gc_type_(collector::kGcTypePartial),
317320
capacity_(capacity),
318321
growth_limit_(growth_limit),
322+
initial_heap_size_(initial_size),
319323
target_footprint_(initial_size),
320324
// Using kPostMonitorLock as a lock at kDefaultMutexLevel is acquired after
321325
// this one.
@@ -2128,6 +2132,27 @@ HomogeneousSpaceCompactResult Heap::PerformHomogeneousSpaceCompact() {
21282132
return HomogeneousSpaceCompactResult::kSuccess;
21292133
}
21302134

2135+
void Heap::SetDefaultConcurrentStartBytes() {
2136+
MutexLock mu(Thread::Current(), *gc_complete_lock_);
2137+
if (collector_type_running_ != kCollectorTypeNone) {
2138+
// If a collector is already running, just let it set concurrent_start_bytes_ .
2139+
return;
2140+
}
2141+
SetDefaultConcurrentStartBytesLocked();
2142+
}
2143+
2144+
void Heap::SetDefaultConcurrentStartBytesLocked() {
2145+
if (IsGcConcurrent()) {
2146+
size_t target_footprint = target_footprint_.load(std::memory_order_relaxed);
2147+
size_t reserve_bytes = target_footprint / 4;
2148+
reserve_bytes = std::min(reserve_bytes, kMaxConcurrentRemainingBytes);
2149+
reserve_bytes = std::max(reserve_bytes, kMinConcurrentRemainingBytes);
2150+
concurrent_start_bytes_ = UnsignedDifference(target_footprint, reserve_bytes);
2151+
} else {
2152+
concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
2153+
}
2154+
}
2155+
21312156
void Heap::ChangeCollector(CollectorType collector_type) {
21322157
// TODO: Only do this with all mutators suspended to avoid races.
21332158
if (collector_type != collector_type_) {
@@ -2174,13 +2199,7 @@ void Heap::ChangeCollector(CollectorType collector_type) {
21742199
UNREACHABLE();
21752200
}
21762201
}
2177-
if (IsGcConcurrent()) {
2178-
concurrent_start_bytes_ =
2179-
UnsignedDifference(target_footprint_.load(std::memory_order_relaxed),
2180-
kMinConcurrentRemainingBytes);
2181-
} else {
2182-
concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
2183-
}
2202+
SetDefaultConcurrentStartBytesLocked();
21842203
}
21852204
}
21862205

@@ -3544,6 +3563,11 @@ double Heap::HeapGrowthMultiplier() const {
35443563

35453564
void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,
35463565
size_t bytes_allocated_before_gc) {
3566+
// We're running in the thread that set collector_type_running_ to something other than none,
3567+
// thus ensuring that there is only one of us running. Thus
3568+
// collector_type_running_ != kCollectorTypeNone, but that's a little tricky to turn into a
3569+
// DCHECK.
3570+
35473571
// We know what our utilization is at this moment.
35483572
// This doesn't actually resize any memory. It just lets the heap grow more when necessary.
35493573
const size_t bytes_allocated = GetBytesAllocated();
@@ -3669,8 +3693,7 @@ void Heap::ClearGrowthLimit() {
36693693
if (target_footprint_.load(std::memory_order_relaxed) == growth_limit_
36703694
&& growth_limit_ < capacity_) {
36713695
target_footprint_.store(capacity_, std::memory_order_relaxed);
3672-
concurrent_start_bytes_ =
3673-
UnsignedDifference(capacity_, kMinConcurrentRemainingBytes);
3696+
SetDefaultConcurrentStartBytes();
36743697
}
36753698
growth_limit_ = capacity_;
36763699
ScopedObjectAccess soa(Thread::Current());
@@ -4437,32 +4460,97 @@ void Heap::VlogHeapGrowth(size_t old_footprint, size_t new_footprint, size_t all
44374460
<< PrettySize(new_footprint) << " for a " << PrettySize(alloc_size) << " allocation";
44384461
}
44394462

4463+
// Run a gc if we haven't run one since initial_gc_num. This forces processes to
4464+
// reclaim memory allocated during startup, even if they don't do much
4465+
// allocation post startup. If the process is actively allocating and triggering
4466+
// GCs, or has moved to the background and hence forced a GC, this does nothing.
44404467
class Heap::TriggerPostForkCCGcTask : public HeapTask {
44414468
public:
4442-
explicit TriggerPostForkCCGcTask(uint64_t target_time) : HeapTask(target_time) {}
4469+
explicit TriggerPostForkCCGcTask(uint64_t target_time, uint32_t initial_gc_num) :
4470+
HeapTask(target_time), initial_gc_num_(initial_gc_num) {}
44434471
void Run(Thread* self) override {
44444472
gc::Heap* heap = Runtime::Current()->GetHeap();
4445-
// Trigger a GC, if not already done. The first GC after fork, whenever it
4446-
// takes place, will adjust the thresholds to normal levels.
4447-
if (heap->target_footprint_.load(std::memory_order_relaxed) == heap->growth_limit_) {
4448-
heap->RequestConcurrentGC(self, kGcCauseBackground, false, heap->GetCurrentGcNum());
4473+
if (heap->GetCurrentGcNum() == initial_gc_num_) {
4474+
if (kLogAllGCs) {
4475+
LOG(INFO) << "Forcing GC for allocation-inactive process";
4476+
}
4477+
heap->RequestConcurrentGC(self, kGcCauseBackground, false, initial_gc_num_);
44494478
}
44504479
}
4480+
private:
4481+
uint32_t initial_gc_num_;
44514482
};
44524483

4484+
// Reduce target footprint, if no GC has occurred since initial_gc_num.
4485+
// If a GC already occurred, it will have done this for us.
4486+
class Heap::ReduceTargetFootprintTask : public HeapTask {
4487+
public:
4488+
explicit ReduceTargetFootprintTask(uint64_t target_time, size_t new_target_sz,
4489+
uint32_t initial_gc_num) :
4490+
HeapTask(target_time), new_target_sz_(new_target_sz), initial_gc_num_(initial_gc_num) {}
4491+
void Run(Thread* self) override {
4492+
gc::Heap* heap = Runtime::Current()->GetHeap();
4493+
MutexLock mu(self, *(heap->gc_complete_lock_));
4494+
if (heap->GetCurrentGcNum() == initial_gc_num_
4495+
&& heap->collector_type_running_ == kCollectorTypeNone) {
4496+
size_t target_footprint = heap->target_footprint_.load(std::memory_order_relaxed);
4497+
if (target_footprint > new_target_sz_) {
4498+
if (heap->target_footprint_.CompareAndSetStrongRelaxed(target_footprint, new_target_sz_)) {
4499+
heap->SetDefaultConcurrentStartBytesLocked();
4500+
}
4501+
}
4502+
}
4503+
}
4504+
private:
4505+
size_t new_target_sz_;
4506+
uint32_t initial_gc_num_;
4507+
};
4508+
4509+
// Return a pseudo-random integer between 0 and 19999, using the uid as a seed. We want this to
4510+
// be deterministic for a given process, but to vary randomly across processes. Empirically, the
4511+
// uids for processes for which this matters are distinct.
4512+
static uint32_t GetPseudoRandomFromUid() {
4513+
std::default_random_engine rng(getuid());
4514+
std::uniform_int_distribution<int> dist(0, 19999);
4515+
return dist(rng);
4516+
}
4517+
44534518
void Heap::PostForkChildAction(Thread* self) {
4519+
uint32_t starting_gc_num = GetCurrentGcNum();
4520+
uint64_t last_adj_time = NanoTime();
4521+
next_gc_type_ = NonStickyGcType(); // Always start with a full gc.
4522+
44544523
// Temporarily increase target_footprint_ and concurrent_start_bytes_ to
44554524
// max values to avoid GC during app launch.
4456-
if (collector_type_ == kCollectorTypeCC && !IsLowMemoryMode()) {
4525+
if (!IsLowMemoryMode()) {
44574526
// Set target_footprint_ to the largest allowed value.
44584527
SetIdealFootprint(growth_limit_);
4459-
// Set concurrent_start_bytes_ to half of the heap size.
4460-
size_t target_footprint = target_footprint_.load(std::memory_order_relaxed);
4461-
concurrent_start_bytes_ = std::max(target_footprint / 2, GetBytesAllocated());
4462-
4463-
GetTaskProcessor()->AddTask(
4464-
self, new TriggerPostForkCCGcTask(NanoTime() + MsToNs(kPostForkMaxHeapDurationMS)));
4528+
SetDefaultConcurrentStartBytes();
4529+
4530+
// Shrink heap after kPostForkMaxHeapDurationMS, to force a memory hog process to GC.
4531+
// This remains high enough that many processes will continue without a GC.
4532+
if (initial_heap_size_ < growth_limit_) {
4533+
size_t first_shrink_size = std::max(growth_limit_ / 4, initial_heap_size_);
4534+
last_adj_time += MsToNs(kPostForkMaxHeapDurationMS);
4535+
GetTaskProcessor()->AddTask(
4536+
self, new ReduceTargetFootprintTask(last_adj_time, first_shrink_size, starting_gc_num));
4537+
// Shrink to a small value after a substantial time period. This will typically force a
4538+
// GC if none has occurred yet. Has no effect if there was a GC before this anyway, which
4539+
// is commonly the case, e.g. because of a process transition.
4540+
if (initial_heap_size_ < first_shrink_size) {
4541+
last_adj_time += MsToNs(4 * kPostForkMaxHeapDurationMS);
4542+
GetTaskProcessor()->AddTask(
4543+
self,
4544+
new ReduceTargetFootprintTask(last_adj_time, initial_heap_size_, starting_gc_num));
4545+
}
4546+
}
44654547
}
4548+
// Schedule a GC after a substantial period of time. This will become a no-op if another GC is
4549+
// scheduled in the interim. If not, we want to avoid holding onto start-up garbage.
4550+
uint64_t post_fork_gc_time = last_adj_time
4551+
+ MsToNs(4 * kPostForkMaxHeapDurationMS + GetPseudoRandomFromUid());
4552+
GetTaskProcessor()->AddTask(self,
4553+
new TriggerPostForkCCGcTask(post_fork_gc_time, starting_gc_num));
44664554
}
44674555

44684556
void Heap::VisitReflectiveTargets(ReflectiveValueVisitor *visit) {

runtime/gc/heap.h

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,9 +330,10 @@ class Heap {
330330
void ChangeAllocator(AllocatorType allocator)
331331
REQUIRES(Locks::mutator_lock_, !Locks::runtime_shutdown_lock_);
332332

333-
// Change the collector to be one of the possible options (MS, CMS, SS).
333+
// Change the collector to be one of the possible options (MS, CMS, SS). Only safe when no
334+
// concurrent accesses to the heap are possible.
334335
void ChangeCollector(CollectorType collector_type)
335-
REQUIRES(Locks::mutator_lock_);
336+
REQUIRES(Locks::mutator_lock_, !*gc_complete_lock_);
336337

337338
// The given reference is believed to be to an object in the Java heap, check the soundness of it.
338339
// TODO: NO_THREAD_SAFETY_ANALYSIS since we call this everywhere and it is impossible to find a
@@ -405,7 +406,7 @@ class Heap {
405406

406407
// Removes the growth limit on the alloc space so it may grow to its maximum capacity. Used to
407408
// implement dalvik.system.VMRuntime.clearGrowthLimit.
408-
void ClearGrowthLimit();
409+
void ClearGrowthLimit() REQUIRES(!*gc_complete_lock_);
409410

410411
// Make the current growth limit the new maximum capacity, unmaps pages at the end of spaces
411412
// which will never be used. Used to implement dalvik.system.VMRuntime.clampGrowthLimit.
@@ -458,6 +459,7 @@ class Heap {
458459

459460
// For the alloc space, sets the maximum number of bytes that the heap is allowed to allocate
460461
// from the system. Doesn't allow the space to exceed its growth limit.
462+
// Set while we hold gc_complete_lock or collector_type_running_ != kCollectorTypeNone.
461463
void SetIdealFootprint(size_t max_allowed_footprint);
462464

463465
// Blocks the caller until the garbage collector becomes idle and returns the type of GC we
@@ -954,7 +956,7 @@ class Heap {
954956

955957
const Verification* GetVerification() const;
956958

957-
void PostForkChildAction(Thread* self);
959+
void PostForkChildAction(Thread* self) REQUIRES(!*gc_complete_lock_);
958960

959961
void TraceHeapSize(size_t heap_size);
960962

@@ -965,6 +967,7 @@ class Heap {
965967
class CollectorTransitionTask;
966968
class HeapTrimTask;
967969
class TriggerPostForkCCGcTask;
970+
class ReduceTargetFootprintTask;
968971

969972
// Compact source space to target space. Returns the collector used.
970973
collector::GarbageCollector* Compact(space::ContinuousMemMapAllocSpace* target_space,
@@ -1171,6 +1174,9 @@ class Heap {
11711174
// the target utilization ratio. This should only be called immediately after a full garbage
11721175
// collection. bytes_allocated_before_gc is used to measure bytes / second for the period which
11731176
// the GC was run.
1177+
// This is only called by the thread that set collector_type_running_ to a value other than
1178+
// kCollectorTypeNone, or while holding gc_complete_lock, and ensuring that
1179+
// collector_type_running_ is kCollectorTypeNone.
11741180
void GrowForUtilization(collector::GarbageCollector* collector_ran,
11751181
size_t bytes_allocated_before_gc = 0)
11761182
REQUIRES(!process_state_update_lock_);
@@ -1263,6 +1269,11 @@ class Heap {
12631269
// of a garbage collection.
12641270
size_t GetNativeBytes();
12651271

1272+
// Set concurrent_start_bytes_ to a reasonable guess, given target_footprint_ .
1273+
void SetDefaultConcurrentStartBytes() REQUIRES(!*gc_complete_lock_);
1274+
// This version assumes no concurrent updaters.
1275+
void SetDefaultConcurrentStartBytesLocked();
1276+
12661277
// All-known continuous spaces, where objects lie within fixed bounds.
12671278
std::vector<space::ContinuousSpace*> continuous_spaces_ GUARDED_BY(Locks::mutator_lock_);
12681279

@@ -1379,6 +1390,9 @@ class Heap {
13791390
// Task processor, proxies heap trim requests to the daemon threads.
13801391
std::unique_ptr<TaskProcessor> task_processor_;
13811392

1393+
// The following are declared volatile only for debugging purposes; it shouldn't otherwise
1394+
// matter.
1395+
13821396
// Collector type of the running GC.
13831397
volatile CollectorType collector_type_running_ GUARDED_BY(gc_complete_lock_);
13841398

@@ -1400,21 +1414,29 @@ class Heap {
14001414
// Only weakly enforced for simultaneous allocations.
14011415
size_t growth_limit_;
14021416

1417+
// Requested initial heap size. Temporarily ignored after a fork, but then reestablished after
1418+
// a while to usually trigger the initial GC.
1419+
size_t initial_heap_size_;
1420+
14031421
// Target size (as in maximum allocatable bytes) for the heap. Weakly enforced as a limit for
14041422
// non-concurrent GC. Used as a guideline for computing concurrent_start_bytes_ in the
1405-
// concurrent GC case.
1423+
// concurrent GC case. Updates normally occur while collector_type_running_ is not none.
14061424
Atomic<size_t> target_footprint_;
14071425

1426+
Mutex process_state_update_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
1427+
14081428
// Computed with foreground-multiplier in GrowForUtilization() when run in
14091429
// jank non-perceptible state. On update to process state from background to
14101430
// foreground we set target_footprint_ to this value.
1411-
Mutex process_state_update_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
14121431
size_t min_foreground_target_footprint_ GUARDED_BY(process_state_update_lock_);
14131432

14141433
// When num_bytes_allocated_ exceeds this amount then a concurrent GC should be requested so that
14151434
// it completes ahead of an allocation failing.
14161435
// A multiple of this is also used to determine when to trigger a GC in response to native
14171436
// allocation.
1437+
// After initialization, this is only updated by the thread that set collector_type_running_ to
1438+
// a value other than kCollectorTypeNone, or while holding gc_complete_lock, and ensuring that
1439+
// collector_type_running_ is kCollectorTypeNone.
14181440
size_t concurrent_start_bytes_;
14191441

14201442
// Since the heap was created, how many bytes have been freed.

0 commit comments

Comments
 (0)