Skip to content

Commit bedb68a

Browse files
author
William Kemper
committed
8342444: Shenandoah: Uncommit regions from a separate, STS aware thread
Reviewed-by: shade, kdnilsen, ysr
1 parent dbf48a5 commit bedb68a

9 files changed

+420
-127
lines changed

src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp

+12-28
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,8 @@ void ShenandoahControlThread::run_service() {
5656
const GCCause::Cause default_cause = GCCause::_shenandoah_concurrent_gc;
5757
int sleep = ShenandoahControlIntervalMin;
5858

59-
double last_shrink_time = os::elapsedTime();
6059
double last_sleep_adjust_time = os::elapsedTime();
6160

62-
// Shrink period avoids constantly polling regions for shrinking.
63-
// Having a period 10x lower than the delay would mean we hit the
64-
// shrinking with lag of less than 1/10-th of true delay.
65-
// ShenandoahUncommitDelay is in msecs, but shrink_period is in seconds.
66-
const double shrink_period = (double)ShenandoahUncommitDelay / 1000 / 10;
67-
6861
ShenandoahCollectorPolicy* const policy = heap->shenandoah_policy();
6962
ShenandoahHeuristics* const heuristics = heap->heuristics();
7063
while (!in_graceful_shutdown() && !should_terminate()) {
@@ -76,9 +69,6 @@ void ShenandoahControlThread::run_service() {
7669
// This control loop iteration has seen this much allocation.
7770
const size_t allocs_seen = reset_allocs_seen();
7871

79-
// Check if we have seen a new target for soft max heap size.
80-
const bool soft_max_changed = heap->check_soft_max_changed();
81-
8272
// Choose which GC mode to run in. The block below should select a single mode.
8373
GCMode mode = none;
8474
GCCause::Cause cause = GCCause::_last_gc_cause;
@@ -136,6 +126,9 @@ void ShenandoahControlThread::run_service() {
136126
assert (!gc_requested || cause != GCCause::_last_gc_cause, "GC cause should be set");
137127

138128
if (gc_requested) {
129+
// Cannot uncommit bitmap slices during concurrent reset
130+
ShenandoahNoUncommitMark forbid_region_uncommit(heap);
131+
139132
// GC is starting, bump the internal ID
140133
update_gc_id();
141134

@@ -238,29 +231,20 @@ void ShenandoahControlThread::run_service() {
238231
}
239232
}
240233

241-
const double current = os::elapsedTime();
242-
243-
if (ShenandoahUncommit && (is_gc_requested || soft_max_changed || (current - last_shrink_time > shrink_period))) {
244-
// Explicit GC tries to uncommit everything down to min capacity.
245-
// Soft max change tries to uncommit everything down to target capacity.
246-
// Periodic uncommit tries to uncommit suitable regions down to min capacity.
247-
248-
double shrink_before = (is_gc_requested || soft_max_changed) ?
249-
current :
250-
current - (ShenandoahUncommitDelay / 1000.0);
251-
252-
size_t shrink_until = soft_max_changed ?
253-
heap->soft_max_capacity() :
254-
heap->min_capacity();
255-
256-
heap->maybe_uncommit(shrink_before, shrink_until);
257-
heap->phase_timings()->flush_cycle_to_global();
258-
last_shrink_time = current;
234+
// Check if we have seen a new target for soft max heap size or if a gc was requested.
235+
// Either of these conditions will attempt to uncommit regions.
236+
if (ShenandoahUncommit) {
237+
if (heap->check_soft_max_changed()) {
238+
heap->notify_soft_max_changed();
239+
} else if (is_gc_requested) {
240+
heap->notify_explicit_gc_requested();
241+
}
259242
}
260243

261244
// Wait before performing the next action. If allocation happened during this wait,
262245
// we exit sooner, to let heuristics re-evaluate new conditions. If we are at idle,
263246
// back off exponentially.
247+
const double current = os::elapsedTime();
264248
if (heap->has_changed()) {
265249
sleep = ShenandoahControlIntervalMin;
266250
} else if ((current - last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){

src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ class ShenandoahResetBitmapTask : public WorkerTask {
7373
WorkerTask("Shenandoah Reset Bitmap"), _generation(generation) {}
7474

7575
void work(uint worker_id) {
76-
ShenandoahHeapRegion* region = _regions.next();
7776
ShenandoahHeap* heap = ShenandoahHeap::heap();
77+
assert(!heap->is_uncommit_in_progress(), "Cannot uncommit bitmaps while resetting them.");
78+
ShenandoahHeapRegion* region = _regions.next();
7879
ShenandoahMarkingContext* const ctx = heap->marking_context();
7980
while (region != nullptr) {
8081
auto const affiliation = region->affiliation();

src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp

+11-25
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,8 @@ void ShenandoahGenerationalControlThread::run_service() {
6767
const GCMode default_mode = concurrent_normal;
6868
ShenandoahGenerationType generation = GLOBAL;
6969

70-
double last_shrink_time = os::elapsedTime();
7170
uint age_period = 0;
7271

73-
// Shrink period avoids constantly polling regions for shrinking.
74-
// Having a period 10x lower than the delay would mean we hit the
75-
// shrinking with lag of less than 1/10-th of true delay.
76-
// ShenandoahUncommitDelay is in msecs, but shrink_period is in seconds.
77-
const double shrink_period = (double)ShenandoahUncommitDelay / 1000 / 10;
78-
7972
ShenandoahCollectorPolicy* const policy = heap->shenandoah_policy();
8073

8174
// Heuristics are notified of allocation failures here and other outcomes
@@ -191,6 +184,9 @@ void ShenandoahGenerationalControlThread::run_service() {
191184
assert (!gc_requested || cause != GCCause::_no_gc, "GC cause should be set");
192185

193186
if (gc_requested) {
187+
// Cannot uncommit bitmap slices during concurrent reset
188+
ShenandoahNoUncommitMark forbid_region_uncommit(heap);
189+
194190
// Blow away all soft references on this cycle, if handling allocation failure,
195191
// either implicit or explicit GC request, or we are requested to do so unconditionally.
196192
if (generation == GLOBAL && (alloc_failure_pending || is_gc_requested || ShenandoahAlwaysClearSoftRefs)) {
@@ -303,24 +299,14 @@ void ShenandoahGenerationalControlThread::run_service() {
303299
}
304300
}
305301

306-
const double current = os::elapsedTime();
307-
308-
if (ShenandoahUncommit && (is_gc_requested || soft_max_changed || (current - last_shrink_time > shrink_period))) {
309-
// Explicit GC tries to uncommit everything down to min capacity.
310-
// Soft max change tries to uncommit everything down to target capacity.
311-
// Periodic uncommit tries to uncommit suitable regions down to min capacity.
312-
313-
double shrink_before = (is_gc_requested || soft_max_changed) ?
314-
current :
315-
current - (ShenandoahUncommitDelay / 1000.0);
316-
317-
size_t shrink_until = soft_max_changed ?
318-
heap->soft_max_capacity() :
319-
heap->min_capacity();
320-
321-
heap->maybe_uncommit(shrink_before, shrink_until);
322-
heap->phase_timings()->flush_cycle_to_global();
323-
last_shrink_time = current;
302+
// Check if we have seen a new target for soft max heap size or if a gc was requested.
303+
// Either of these conditions will attempt to uncommit regions.
304+
if (ShenandoahUncommit) {
305+
if (heap->check_soft_max_changed()) {
306+
heap->notify_soft_max_changed();
307+
} else if (is_gc_requested) {
308+
heap->notify_explicit_gc_requested();
309+
}
324310
}
325311

326312
// Wait for ShenandoahControlIntervalMax unless there was an allocation failure or another request was made mid-cycle.

src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp

+54-62
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
*/
2626

2727
#include "precompiled.hpp"
28-
#include "memory/allocation.hpp"
29-
#include "memory/universe.hpp"
28+
29+
#include "cds/archiveHeapWriter.hpp"
30+
#include "classfile/systemDictionary.hpp"
31+
#include "code/codeCache.hpp"
3032

3133
#include "gc/shared/classUnloadingContext.hpp"
3234
#include "gc/shared/fullGCForwarding.hpp"
@@ -42,17 +44,16 @@
4244
#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp"
4345
#include "gc/shenandoah/shenandoahAllocRequest.hpp"
4446
#include "gc/shenandoah/shenandoahBarrierSet.hpp"
45-
#include "gc/shenandoah/shenandoahClosures.inline.hpp"
47+
#include "gc/shenandoah/shenandoahCodeRoots.hpp"
4648
#include "gc/shenandoah/shenandoahCollectionSet.hpp"
4749
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
4850
#include "gc/shenandoah/shenandoahConcurrentMark.hpp"
49-
#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
5051
#include "gc/shenandoah/shenandoahControlThread.hpp"
52+
#include "gc/shenandoah/shenandoahClosures.inline.hpp"
5153
#include "gc/shenandoah/shenandoahFreeSet.hpp"
5254
#include "gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp"
5355
#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
5456
#include "gc/shenandoah/shenandoahGlobalGeneration.hpp"
55-
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
5657
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
5758
#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp"
5859
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
@@ -65,31 +66,31 @@
6566
#include "gc/shenandoah/shenandoahPacer.inline.hpp"
6667
#include "gc/shenandoah/shenandoahPadding.hpp"
6768
#include "gc/shenandoah/shenandoahParallelCleaning.inline.hpp"
69+
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
6870
#include "gc/shenandoah/shenandoahReferenceProcessor.hpp"
6971
#include "gc/shenandoah/shenandoahRootProcessor.inline.hpp"
7072
#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp"
7173
#include "gc/shenandoah/shenandoahSTWMark.hpp"
74+
#include "gc/shenandoah/shenandoahUncommitThread.hpp"
7275
#include "gc/shenandoah/shenandoahUtils.hpp"
7376
#include "gc/shenandoah/shenandoahVerifier.hpp"
74-
#include "gc/shenandoah/shenandoahCodeRoots.hpp"
7577
#include "gc/shenandoah/shenandoahVMOperations.hpp"
7678
#include "gc/shenandoah/shenandoahWorkGroup.hpp"
7779
#include "gc/shenandoah/shenandoahWorkerPolicy.hpp"
7880
#include "gc/shenandoah/shenandoahYoungGeneration.hpp"
7981
#include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp"
8082
#include "gc/shenandoah/mode/shenandoahPassiveMode.hpp"
8183
#include "gc/shenandoah/mode/shenandoahSATBMode.hpp"
82-
#include "utilities/globalDefinitions.hpp"
8384

8485
#if INCLUDE_JFR
8586
#include "gc/shenandoah/shenandoahJfrSupport.hpp"
8687
#endif
8788

88-
#include "cds/archiveHeapWriter.hpp"
89-
#include "classfile/systemDictionary.hpp"
90-
#include "code/codeCache.hpp"
89+
90+
#include "memory/allocation.hpp"
9191
#include "memory/classLoaderMetaspace.hpp"
9292
#include "memory/metaspaceUtils.hpp"
93+
#include "memory/universe.hpp"
9394
#include "nmt/mallocTracker.hpp"
9495
#include "nmt/memTracker.hpp"
9596
#include "oops/compressedOops.inline.hpp"
@@ -102,6 +103,7 @@
102103
#include "runtime/safepointMechanism.hpp"
103104
#include "runtime/stackWatermarkSet.hpp"
104105
#include "runtime/vmThread.hpp"
106+
#include "utilities/globalDefinitions.hpp"
105107
#include "utilities/events.hpp"
106108
#include "utilities/powerOfTwo.hpp"
107109

@@ -459,6 +461,10 @@ jint ShenandoahHeap::initialize() {
459461

460462
initialize_controller();
461463

464+
if (ShenandoahUncommit) {
465+
_uncommit_thread = new ShenandoahUncommitThread(this);
466+
}
467+
462468
print_init_logger();
463469

464470
FullGCForwarding::initialize(_heap_region);
@@ -530,6 +536,7 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) :
530536
_update_refs_iterator(this),
531537
_global_generation(nullptr),
532538
_control_thread(nullptr),
539+
_uncommit_thread(nullptr),
533540
_young_generation(nullptr),
534541
_old_generation(nullptr),
535542
_shenandoah_policy(policy),
@@ -800,60 +807,15 @@ bool ShenandoahHeap::is_in(const void* p) const {
800807
}
801808
}
802809

803-
void ShenandoahHeap::maybe_uncommit(double shrink_before, size_t shrink_until) {
804-
assert (ShenandoahUncommit, "should be enabled");
805-
806-
// Determine if there is work to do. This avoids taking heap lock if there is
807-
// no work available, avoids spamming logs with superfluous logging messages,
808-
// and minimises the amount of work while locks are taken.
809-
810-
if (committed() <= shrink_until) return;
811-
812-
bool has_work = false;
813-
for (size_t i = 0; i < num_regions(); i++) {
814-
ShenandoahHeapRegion* r = get_region(i);
815-
if (r->is_empty_committed() && (r->empty_time() < shrink_before)) {
816-
has_work = true;
817-
break;
818-
}
819-
}
820-
821-
if (has_work) {
822-
static const char* msg = "Concurrent uncommit";
823-
ShenandoahConcurrentPhase gcPhase(msg, ShenandoahPhaseTimings::conc_uncommit, true /* log_heap_usage */);
824-
EventMark em("%s", msg);
825-
826-
op_uncommit(shrink_before, shrink_until);
810+
void ShenandoahHeap::notify_soft_max_changed() {
811+
if (_uncommit_thread != nullptr) {
812+
_uncommit_thread->notify_soft_max_changed();
827813
}
828814
}
829815

830-
void ShenandoahHeap::op_uncommit(double shrink_before, size_t shrink_until) {
831-
assert (ShenandoahUncommit, "should be enabled");
832-
833-
// Application allocates from the beginning of the heap, and GC allocates at
834-
// the end of it. It is more efficient to uncommit from the end, so that applications
835-
// could enjoy the near committed regions. GC allocations are much less frequent,
836-
// and therefore can accept the committing costs.
837-
838-
size_t count = 0;
839-
for (size_t i = num_regions(); i > 0; i--) { // care about size_t underflow
840-
ShenandoahHeapRegion* r = get_region(i - 1);
841-
if (r->is_empty_committed() && (r->empty_time() < shrink_before)) {
842-
ShenandoahHeapLocker locker(lock());
843-
if (r->is_empty_committed()) {
844-
if (committed() < shrink_until + ShenandoahHeapRegion::region_size_bytes()) {
845-
break;
846-
}
847-
848-
r->make_uncommitted();
849-
count++;
850-
}
851-
}
852-
SpinPause(); // allow allocators to take the lock
853-
}
854-
855-
if (count > 0) {
856-
notify_heap_changed();
816+
void ShenandoahHeap::notify_explicit_gc_requested() {
817+
if (_uncommit_thread != nullptr) {
818+
_uncommit_thread->notify_explicit_gc_requested();
857819
}
858820
}
859821

@@ -1507,6 +1469,10 @@ void ShenandoahHeap::gc_threads_do(ThreadClosure* tcl) const {
15071469
tcl->do_thread(_control_thread);
15081470
}
15091471

1472+
if (_uncommit_thread != nullptr) {
1473+
tcl->do_thread(_uncommit_thread);
1474+
}
1475+
15101476
workers()->threads_do(tcl);
15111477
if (_safepoint_workers != nullptr) {
15121478
_safepoint_workers->threads_do(tcl);
@@ -2094,6 +2060,11 @@ void ShenandoahHeap::stop() {
20942060

20952061
// Step 3. Wait until GC worker exits normally.
20962062
control_thread()->stop();
2063+
2064+
// Stop 4. Shutdown uncommit thread.
2065+
if (_uncommit_thread != nullptr) {
2066+
_uncommit_thread->stop();
2067+
}
20972068
}
20982069

20992070
void ShenandoahHeap::stw_unload_classes(bool full_gc) {
@@ -2521,7 +2492,7 @@ bool ShenandoahHeap::uncommit_bitmap_slice(ShenandoahHeapRegion *r) {
25212492

25222493
if (is_bitmap_slice_committed(r, true)) {
25232494
// Some other region from the group is still committed, meaning the bitmap
2524-
// slice is should stay committed, exit right away.
2495+
// slice should stay committed, exit right away.
25252496
return true;
25262497
}
25272498

@@ -2535,6 +2506,27 @@ bool ShenandoahHeap::uncommit_bitmap_slice(ShenandoahHeapRegion *r) {
25352506
return true;
25362507
}
25372508

2509+
void ShenandoahHeap::forbid_uncommit() {
2510+
if (_uncommit_thread != nullptr) {
2511+
_uncommit_thread->forbid_uncommit();
2512+
}
2513+
}
2514+
2515+
void ShenandoahHeap::allow_uncommit() {
2516+
if (_uncommit_thread != nullptr) {
2517+
_uncommit_thread->allow_uncommit();
2518+
}
2519+
}
2520+
2521+
#ifdef ASSERT
2522+
bool ShenandoahHeap::is_uncommit_in_progress() {
2523+
if (_uncommit_thread != nullptr) {
2524+
return _uncommit_thread->is_uncommit_in_progress();
2525+
}
2526+
return false;
2527+
}
2528+
#endif
2529+
25382530
void ShenandoahHeap::safepoint_synchronize_begin() {
25392531
StackWatermarkSet::safepoint_synchronize_begin();
25402532
SuspendibleThreadSet::synchronize();

0 commit comments

Comments
 (0)