Skip to content

Commit 79bbb5a

Browse files
author
William Kemper
committedApr 18, 2023
Account for humongous object waste
Reviewed-by: ysr, kdnilsen
1 parent 42da31c commit 79bbb5a

8 files changed

+102
-33
lines changed
 

‎src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp

+15-5
Original file line numberDiff line numberDiff line change
@@ -579,13 +579,23 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) {
579579
PTR_FORMAT " at transition from FREE to %s",
580580
p2i(r->bottom()), p2i(r->end()), p2i(ctx->top_bitmap(r)), req.affiliation_name());
581581

582+
// While individual regions report their true use, all humongous regions are marked used in the free set.
582583
_mutator_free_bitmap.clear_bit(r->index());
583584
}
584-
585-
// While individual regions report their true use, all humongous regions are
586-
// marked used in the free set.
587-
increase_used(ShenandoahHeapRegion::region_size_bytes() * num);
588-
generation->increase_used(words_size * HeapWordSize);
585+
size_t total_humongous_size = ShenandoahHeapRegion::region_size_bytes() * num;
586+
increase_used(total_humongous_size);
587+
if (_heap->mode()->is_generational()) {
588+
size_t humongous_waste = total_humongous_size - words_size * HeapWordSize;
589+
_heap->global_generation()->increase_used(words_size * HeapWordSize);
590+
_heap->global_generation()->increase_humongous_waste(humongous_waste);
591+
if (req.is_young()) {
592+
_heap->young_generation()->increase_used(words_size * HeapWordSize);
593+
_heap->young_generation()->increase_humongous_waste(humongous_waste);
594+
} else if (req.is_old()) {
595+
_heap->old_generation()->increase_used(words_size * HeapWordSize);
596+
_heap->old_generation()->increase_humongous_waste(humongous_waste);
597+
}
598+
}
589599

590600
if (remainder != 0) {
591601
// Record this remainder as allocation waste

‎src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class ShenandoahFreeSet : public CHeapObj<mtGC> {
6868
void adjust_bounds();
6969
bool touches_bounds(size_t num) const;
7070

71+
// Used of free set represents the amount of is_mutator_free set that has been consumed since most recent rebuild.
7172
void increase_used(size_t amount);
7273
void clear_internal();
7374

‎src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,7 @@ void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) {
202202
// Defer unadjust_available() invocations until after Full GC finishes its efforts because Full GC makes use
203203
// of young-gen memory that may have been loaned from old-gen.
204204

205-
// No need to old_gen->increase_used(). That was done when plabs were allocated, accounting for both old evacs and promotions.
206-
205+
// No need for old_gen->increase_used() as this was done when plabs were allocated.
207206
heap->set_alloc_supplement_reserve(0);
208207
heap->set_young_evac_reserve(0);
209208
heap->set_old_evac_reserve(0);
@@ -1502,10 +1501,9 @@ void ShenandoahFullGC::phase5_epilog() {
15021501
byte_size_in_proper_unit(heap->young_generation()->used()), proper_unit_for_byte_size(heap->young_generation()->used()),
15031502
byte_size_in_proper_unit(heap->old_generation()->used()), proper_unit_for_byte_size(heap->old_generation()->used()));
15041503
}
1505-
15061504
heap->collection_set()->clear();
15071505
heap->free_set()->rebuild();
15081506
}
1509-
15101507
heap->clear_cancelled_gc(true /* clear oom handler */);
15111508
}
1509+

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

+29-16
Original file line numberDiff line numberDiff line change
@@ -162,17 +162,16 @@ void ShenandoahGeneration::log_status(const char *msg) const {
162162
size_t v_soft_max_capacity = soft_max_capacity();
163163
size_t v_max_capacity = max_capacity();
164164
size_t v_available = available();
165-
size_t v_adjusted_avail = adjusted_available();
165+
size_t v_humongous_waste = get_humongous_waste();
166166
LogGcInfo::print("%s: %s generation used: " SIZE_FORMAT "%s, used regions: " SIZE_FORMAT "%s, "
167-
"soft capacity: " SIZE_FORMAT "%s, max capacity: " SIZE_FORMAT "%s, available: " SIZE_FORMAT "%s, "
168-
"adjusted available: " SIZE_FORMAT "%s",
169-
msg, name(),
167+
"humongous waste: " SIZE_FORMAT "%s, soft capacity: " SIZE_FORMAT "%s, max capacity: " SIZE_FORMAT "%s, "
168+
"available: " SIZE_FORMAT "%s", msg, name(),
170169
byte_size_in_proper_unit(v_used), proper_unit_for_byte_size(v_used),
171170
byte_size_in_proper_unit(v_used_regions), proper_unit_for_byte_size(v_used_regions),
171+
byte_size_in_proper_unit(v_humongous_waste), proper_unit_for_byte_size(v_humongous_waste),
172172
byte_size_in_proper_unit(v_soft_max_capacity), proper_unit_for_byte_size(v_soft_max_capacity),
173173
byte_size_in_proper_unit(v_max_capacity), proper_unit_for_byte_size(v_max_capacity),
174-
byte_size_in_proper_unit(v_available), proper_unit_for_byte_size(v_available),
175-
byte_size_in_proper_unit(v_adjusted_avail), proper_unit_for_byte_size(v_adjusted_avail));
174+
byte_size_in_proper_unit(v_available), proper_unit_for_byte_size(v_available));
176175
}
177176

178177
void ShenandoahGeneration::reset_mark_bitmap() {
@@ -893,7 +892,7 @@ ShenandoahGeneration::ShenandoahGeneration(ShenandoahGenerationType type,
893892
_task_queues(new ShenandoahObjToScanQueueSet(max_workers)),
894893
_ref_processor(new ShenandoahReferenceProcessor(MAX2(max_workers, 1U))),
895894
_collection_thread_time_s(0.0),
896-
_affiliated_region_count(0), _used(0), _bytes_allocated_since_gc_start(0),
895+
_affiliated_region_count(0), _humongous_waste(0), _used(0), _bytes_allocated_since_gc_start(0),
897896
_max_capacity(max_capacity), _soft_max_capacity(soft_max_capacity),
898897
_adjusted_capacity(soft_max_capacity), _heuristics(nullptr) {
899898
_is_marking_complete.set();
@@ -957,13 +956,15 @@ size_t ShenandoahGeneration::decrement_affiliated_region_count() {
957956
}
958957

959958
void ShenandoahGeneration::establish_usage(size_t num_regions, size_t num_bytes, size_t humongous_waste) {
959+
assert(ShenandoahHeap::heap()->mode()->is_generational(), "Only generational mode accounts for generational usage");
960960
assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "must be at a safepoint");
961961
_affiliated_region_count = num_regions;
962962
_used = num_bytes;
963-
// future improvement: _humongous_waste = humongous_waste;
963+
_humongous_waste = humongous_waste;
964964
}
965965

966966
void ShenandoahGeneration::clear_used() {
967+
assert(ShenandoahHeap::heap()->mode()->is_generational(), "Only generational mode accounts for generational usage");
967968
assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "must be at a safepoint");
968969
// Do this atomically to assure visibility to other threads, even though these other threads may be idle "right now"..
969970
Atomic::store(&_used, (size_t)0);
@@ -978,6 +979,23 @@ void ShenandoahGeneration::decrease_used(size_t bytes) {
978979
Atomic::sub(&_used, bytes);
979980
}
980981

982+
void ShenandoahGeneration::increase_humongous_waste(size_t bytes) {
983+
if (bytes > 0) {
984+
shenandoah_assert_heaplocked_or_fullgc_safepoint();
985+
_humongous_waste += bytes;
986+
}
987+
}
988+
989+
void ShenandoahGeneration::decrease_humongous_waste(size_t bytes) {
990+
if (bytes > 0) {
991+
shenandoah_assert_heaplocked_or_fullgc_safepoint();
992+
assert(_humongous_waste >= bytes, "Waste cannot be negative");
993+
assert(ShenandoahHeap::heap()->is_full_gc_in_progress() || (_humongous_waste >= bytes),
994+
"Waste (" SIZE_FORMAT ") cannot be negative (after subtracting " SIZE_FORMAT ")", _humongous_waste, bytes);
995+
_humongous_waste -= bytes;
996+
}
997+
}
998+
981999
size_t ShenandoahGeneration::used_regions() const {
9821000
return _affiliated_region_count;
9831001
}
@@ -997,19 +1015,14 @@ size_t ShenandoahGeneration::used_regions_size() const {
9971015
}
9981016

9991017
size_t ShenandoahGeneration::available() const {
1000-
size_t in_use = used();
1018+
size_t in_use = used() + get_humongous_waste();
10011019
size_t soft_capacity = soft_max_capacity();
10021020
return in_use > soft_capacity ? 0 : soft_capacity - in_use;
10031021
}
10041022

10051023
size_t ShenandoahGeneration::adjust_available(intptr_t adjustment) {
1006-
// TODO: ysr: remove this check & warning
1007-
if (adjustment % ShenandoahHeapRegion::region_size_bytes() != 0) {
1008-
log_warning(gc)("Adjustment (" INTPTR_FORMAT ") should be a multiple of region size (" SIZE_FORMAT ")",
1009-
adjustment, ShenandoahHeapRegion::region_size_bytes());
1010-
}
10111024
assert(adjustment % ShenandoahHeapRegion::region_size_bytes() == 0,
1012-
"Adjustment to generation size must be multiple of region size");
1025+
"Adjustment to generation size must be multiple of region size");
10131026
_adjusted_capacity = soft_max_capacity() + adjustment;
10141027
return _adjusted_capacity;
10151028
}
@@ -1020,7 +1033,7 @@ size_t ShenandoahGeneration::unadjust_available() {
10201033
}
10211034

10221035
size_t ShenandoahGeneration::adjusted_available() const {
1023-
size_t in_use = used();
1036+
size_t in_use = used() + get_humongous_waste();
10241037
size_t capacity = _adjusted_capacity;
10251038
return in_use > capacity ? 0 : capacity - in_use;
10261039
}

‎src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp

+12
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ class ShenandoahGeneration : public CHeapObj<mtGC> {
5252
protected:
5353
// Usage
5454
size_t _affiliated_region_count;
55+
56+
// How much free memory is left in the last region of humongous objects.
57+
// This is _not_ included in used, but it _is_ deducted from available,
58+
// which gives the heuristics a more accurate view of how much memory remains
59+
// for allocation. This figure is also included the heap status logging.
60+
// The units are bytes. The value is only changed on a safepoint or under the
61+
// heap lock.
62+
size_t _humongous_waste;
5563
volatile size_t _used;
5664
volatile size_t _bytes_allocated_since_gc_start;
5765
size_t _max_capacity;
@@ -196,6 +204,10 @@ class ShenandoahGeneration : public CHeapObj<mtGC> {
196204
void increase_used(size_t bytes);
197205
void decrease_used(size_t bytes);
198206

207+
void increase_humongous_waste(size_t bytes);
208+
void decrease_humongous_waste(size_t bytes);
209+
size_t get_humongous_waste() const { return _humongous_waste; }
210+
199211
virtual bool is_concurrent_mark_in_progress() = 0;
200212
void confirm_heuristics_mode();
201213

‎src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp

+29-5
Original file line numberDiff line numberDiff line change
@@ -282,11 +282,17 @@ void ShenandoahHeapRegion::make_trash() {
282282
shenandoah_assert_heaplocked();
283283
reset_age();
284284
switch (_state) {
285-
case _cset:
286-
// Reclaiming cset regions
287285
case _humongous_start:
288286
case _humongous_cont:
289-
// Reclaiming humongous regions
287+
{
288+
// Reclaiming humongous regions and reclaim humongous waste. When this region is eventually recycled, we'll reclaim
289+
// its used memory. At recycle time, we no longer recognize this as a humongous region.
290+
if (ShenandoahHeap::heap()->mode()->is_generational()) {
291+
decrement_humongous_waste();
292+
}
293+
}
294+
case _cset:
295+
// Reclaiming cset regions
290296
case _regular:
291297
// Immediate region reclaim
292298
set_state(_trash);
@@ -655,7 +661,9 @@ void ShenandoahHeapRegion::recycle() {
655661
ShenandoahHeap* heap = ShenandoahHeap::heap();
656662
shenandoah_assert_heaplocked();
657663

658-
heap->generation_for(affiliation())->decrease_used(used());
664+
if (ShenandoahHeap::heap()->mode()->is_generational()) {
665+
heap->generation_for(affiliation())->decrease_used(used());
666+
}
659667

660668
set_top(bottom());
661669
clear_live_data();
@@ -1036,9 +1044,16 @@ size_t ShenandoahHeapRegion::promote_humongous() {
10361044
log_debug(gc)("promoting humongous region " SIZE_FORMAT ", from " PTR_FORMAT " to " PTR_FORMAT,
10371045
r->index(), p2i(r->bottom()), p2i(r->top()));
10381046
// We mark the entire humongous object's range as dirty after loop terminates, so no need to dirty the range here
1039-
r->set_affiliation(OLD_GENERATION);
10401047
old_generation->increase_used(r->used());
10411048
young_generation->decrease_used(r->used());
1049+
r->set_affiliation(OLD_GENERATION);
1050+
}
1051+
1052+
ShenandoahHeapRegion* tail = heap->get_region(index_limit - 1);
1053+
size_t waste = tail->free();
1054+
if (waste != 0) {
1055+
old_generation->increase_humongous_waste(waste);
1056+
young_generation->decrease_humongous_waste(waste);
10421057
}
10431058
// Then fall through to finish the promotion after releasing the heap lock.
10441059
} else {
@@ -1073,3 +1088,12 @@ size_t ShenandoahHeapRegion::promote_humongous() {
10731088
}
10741089
return index_limit - index();
10751090
}
1091+
1092+
void ShenandoahHeapRegion::decrement_humongous_waste() const {
1093+
assert(is_humongous(), "Should only use this for humongous regions");
1094+
size_t waste_bytes = free();
1095+
if (waste_bytes > 0) {
1096+
ShenandoahHeap::heap()->generation_for(affiliation())->decrease_humongous_waste(waste_bytes);
1097+
ShenandoahHeap::heap()->global_generation()->decrease_humongous_waste(waste_bytes);
1098+
}
1099+
}

‎src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ class ShenandoahHeapRegion {
461461
size_t promote_humongous();
462462

463463
private:
464+
void decrement_humongous_waste() const;
464465
void do_commit();
465466
void do_uncommit();
466467

‎src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp

+13-3
Original file line numberDiff line numberDiff line change
@@ -347,14 +347,17 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
347347
// a subset (e.g. the young generation or old generation) of the total heap.
348348
class ShenandoahCalculateRegionStatsClosure : public ShenandoahHeapRegionClosure {
349349
private:
350-
size_t _used, _committed, _garbage, _regions;
350+
size_t _used, _committed, _garbage, _regions, _humongous_waste;
351351
public:
352-
ShenandoahCalculateRegionStatsClosure() : _used(0), _committed(0), _garbage(0), _regions(0) {};
352+
ShenandoahCalculateRegionStatsClosure() : _used(0), _committed(0), _garbage(0), _regions(0), _humongous_waste(0) {};
353353

354354
void heap_region_do(ShenandoahHeapRegion* r) {
355355
_used += r->used();
356356
_garbage += r->garbage();
357357
_committed += r->is_committed() ? ShenandoahHeapRegion::region_size_bytes() : 0;
358+
if (r->is_humongous()) {
359+
_humongous_waste += r->free();
360+
}
358361
_regions++;
359362
log_debug(gc)("ShenandoahCalculateRegionStatsClosure: adding " SIZE_FORMAT " for %s Region " SIZE_FORMAT ", yielding: " SIZE_FORMAT,
360363
r->used(), (r->is_humongous() ? "humongous" : "regular"), r->index(), _used);
@@ -364,6 +367,7 @@ class ShenandoahCalculateRegionStatsClosure : public ShenandoahHeapRegionClosure
364367
size_t committed() { return _committed; }
365368
size_t garbage() { return _garbage; }
366369
size_t regions() { return _regions; }
370+
size_t waste() { return _humongous_waste; }
367371

368372
// span is the total memory affiliated with these stats (some of which is in use and other is available)
369373
size_t span() { return _regions * ShenandoahHeapRegion::region_size_bytes(); }
@@ -402,7 +406,7 @@ class ShenandoahGenerationStatsClosure : public ShenandoahHeapRegionClosure {
402406
static void validate_usage(const char* label, ShenandoahGeneration* generation, ShenandoahCalculateRegionStatsClosure& stats) {
403407
size_t generation_used = generation->used();
404408
guarantee(stats.used() == generation_used,
405-
"%s: generation (%s) used size must be consistent: generation-used = " SIZE_FORMAT "%s, regions-used = " SIZE_FORMAT "%s",
409+
"%s: generation (%s) used size must be consistent: generation-used: " SIZE_FORMAT "%s, regions-used: " SIZE_FORMAT "%s",
406410
label, generation->name(),
407411
byte_size_in_proper_unit(generation_used), proper_unit_for_byte_size(generation_used),
408412
byte_size_in_proper_unit(stats.used()), proper_unit_for_byte_size(stats.used()));
@@ -418,6 +422,12 @@ class ShenandoahGenerationStatsClosure : public ShenandoahHeapRegionClosure {
418422
// label, generation->name(), stats.regions(),
419423
// byte_size_in_proper_unit(capacity), proper_unit_for_byte_size(capacity));
420424

425+
size_t humongous_waste = generation->get_humongous_waste();
426+
guarantee(stats.waste() == humongous_waste,
427+
"%s: generation (%s) humongous waste must be consistent: generation: " SIZE_FORMAT "%s, regions: " SIZE_FORMAT "%s",
428+
label, generation->name(),
429+
byte_size_in_proper_unit(humongous_waste), proper_unit_for_byte_size(humongous_waste),
430+
byte_size_in_proper_unit(stats.waste()), proper_unit_for_byte_size(stats.waste()));
421431
}
422432
};
423433

0 commit comments

Comments
 (0)
Please sign in to comment.