Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
8245511: G1 adaptive IHOP does not account for reclamation of humongo…
…us objects by young GC

Discount humongous object eager reclaim in IHOP allocation rate.

Reviewed-by: tschatzl, sjohanss
  • Loading branch information
ziyiluo authored and Thomas Schatzl committed Aug 21, 2020
1 parent 24ea96f commit 951b12a622507688c1bf9d9746ffde96b20d38c9
@@ -873,7 +873,7 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {
if (result != NULL) {
size_t size_in_regions = humongous_obj_size_in_regions(word_size);
policy()->old_gen_alloc_tracker()->
add_allocated_bytes_since_last_gc(size_in_regions * HeapRegion::GrainBytes);
add_allocated_humongous_bytes_since_last_gc(size_in_regions * HeapRegion::GrainBytes);
return result;
}

@@ -893,6 +893,9 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {
assert(succeeded, "only way to get back a non-NULL result");
log_trace(gc, alloc)("%s: Successfully scheduled collection returning " PTR_FORMAT,
Thread::current()->name(), p2i(result));
size_t size_in_regions = humongous_obj_size_in_regions(word_size);
policy()->old_gen_alloc_tracker()->
record_collection_pause_humongous_allocation(size_in_regions * HeapRegion::GrainBytes);
return result;
}

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,11 +29,12 @@
#include "gc/g1/g1Trace.hpp"
#include "logging/log.hpp"

G1IHOPControl::G1IHOPControl(double initial_ihop_percent) :
G1IHOPControl::G1IHOPControl(double initial_ihop_percent,
G1OldGenAllocationTracker const* old_gen_alloc_tracker) :
_initial_ihop_percent(initial_ihop_percent),
_target_occupancy(0),
_last_allocation_time_s(0.0),
_last_allocated_bytes(0)
_old_gen_alloc_tracker(old_gen_alloc_tracker)
{
assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0, "Initial IHOP value must be between 0 and 100 but is %.3f", initial_ihop_percent);
}
@@ -44,11 +45,10 @@ void G1IHOPControl::update_target_occupancy(size_t new_target_occupancy) {
_target_occupancy = new_target_occupancy;
}

void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size) {
void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t additional_buffer_size) {
assert(allocation_time_s >= 0.0, "Allocation time must be positive but is %.3f", allocation_time_s);

_last_allocation_time_s = allocation_time_s;
_last_allocated_bytes = allocated_bytes;
}

void G1IHOPControl::print() {
@@ -60,9 +60,9 @@ void G1IHOPControl::print() {
percent_of(cur_conc_mark_start_threshold, _target_occupancy),
_target_occupancy,
G1CollectedHeap::heap()->used(),
_last_allocated_bytes,
_old_gen_alloc_tracker->last_period_old_gen_bytes(),
_last_allocation_time_s * 1000.0,
_last_allocation_time_s > 0.0 ? _last_allocated_bytes / _last_allocation_time_s : 0.0,
_last_allocation_time_s > 0.0 ? _old_gen_alloc_tracker->last_period_old_gen_bytes() / _last_allocation_time_s : 0.0,
last_marking_length_s() * 1000.0);
}

@@ -71,21 +71,23 @@ void G1IHOPControl::send_trace_event(G1NewTracer* tracer) {
tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(),
_target_occupancy,
G1CollectedHeap::heap()->used(),
_last_allocated_bytes,
_old_gen_alloc_tracker->last_period_old_gen_bytes(),
_last_allocation_time_s,
last_marking_length_s());
}

G1StaticIHOPControl::G1StaticIHOPControl(double ihop_percent) :
G1IHOPControl(ihop_percent),
G1StaticIHOPControl::G1StaticIHOPControl(double ihop_percent,
G1OldGenAllocationTracker const* old_gen_alloc_tracker) :
G1IHOPControl(ihop_percent, old_gen_alloc_tracker),
_last_marking_length_s(0.0) {
}

G1AdaptiveIHOPControl::G1AdaptiveIHOPControl(double ihop_percent,
G1OldGenAllocationTracker const* old_gen_alloc_tracker,
G1Predictions const* predictor,
size_t heap_reserve_percent,
size_t heap_waste_percent) :
G1IHOPControl(ihop_percent),
G1IHOPControl(ihop_percent, old_gen_alloc_tracker),
_heap_reserve_percent(heap_reserve_percent),
_heap_waste_percent(heap_waste_percent),
_predictor(predictor),
@@ -145,13 +147,16 @@ size_t G1AdaptiveIHOPControl::get_conc_mark_start_threshold() {
}
}

double G1AdaptiveIHOPControl::last_mutator_period_old_allocation_rate() const {
assert(_last_allocation_time_s > 0, "This should not be called when the last GC is full");

return _old_gen_alloc_tracker->last_period_old_gen_growth() / _last_allocation_time_s;
}

void G1AdaptiveIHOPControl::update_allocation_info(double allocation_time_s,
size_t allocated_bytes,
size_t additional_buffer_size) {
G1IHOPControl::update_allocation_info(allocation_time_s, allocated_bytes, additional_buffer_size);

double allocation_rate = (double) allocated_bytes / allocation_time_s;
_allocation_rate_s.add(allocation_rate);
G1IHOPControl::update_allocation_info(allocation_time_s, additional_buffer_size);
_allocation_rate_s.add(last_mutator_period_old_allocation_rate());

_last_unrestrained_young_size = additional_buffer_size;
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
#ifndef SHARE_GC_G1_G1IHOPCONTROL_HPP
#define SHARE_GC_G1_G1IHOPCONTROL_HPP

#include "gc/g1/g1OldGenAllocationTracker.hpp"
#include "memory/allocation.hpp"
#include "utilities/numberSeq.hpp"

@@ -44,12 +45,12 @@ class G1IHOPControl : public CHeapObj<mtGC> {

// Most recent complete mutator allocation period in seconds.
double _last_allocation_time_s;
// Amount of bytes allocated during _last_allocation_time_s.
size_t _last_allocated_bytes;

// Initialize an instance with the initial IHOP value in percent. The target
// occupancy will be updated at the first heap expansion.
G1IHOPControl(double initial_ihop_percent);
const G1OldGenAllocationTracker* _old_gen_alloc_tracker;
// Initialize an instance with the old gen allocation tracker and the
// initial IHOP value in percent. The target occupancy will be updated
// at the first heap expansion.
G1IHOPControl(double ihop_percent, G1OldGenAllocationTracker const* old_gen_alloc_tracker);

// Most recent time from the end of the concurrent start to the start of the first
// mixed gc.
@@ -70,7 +71,7 @@ class G1IHOPControl : public CHeapObj<mtGC> {
// Together with the target occupancy, this additional buffer should contain the
// difference between old gen size and total heap size at the start of reclamation,
// and space required for that reclamation.
virtual void update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size);
virtual void update_allocation_info(double allocation_time_s, size_t additional_buffer_size);
// Update the time spent in the mutator beginning from the end of concurrent start to
// the first mixed gc.
virtual void update_marking_length(double marking_length_s) = 0;
@@ -88,7 +89,7 @@ class G1StaticIHOPControl : public G1IHOPControl {
protected:
double last_marking_length_s() const { return _last_marking_length_s; }
public:
G1StaticIHOPControl(double ihop_percent);
G1StaticIHOPControl(double ihop_percent, G1OldGenAllocationTracker const* old_gen_alloc_tracker);

size_t get_conc_mark_start_threshold() {
guarantee(_target_occupancy > 0, "Target occupancy must have been initialized.");
@@ -132,17 +133,22 @@ class G1AdaptiveIHOPControl : public G1IHOPControl {
// end of marking. This is typically lower than the requested threshold, as the
// algorithm needs to consider restrictions by the environment.
size_t actual_target_threshold() const;

// This method calculates the old gen allocation rate based on the net survived
// bytes that are allocated in the old generation in the last mutator period.
double last_mutator_period_old_allocation_rate() const;
protected:
virtual double last_marking_length_s() const { return _marking_times_s.last(); }
public:
G1AdaptiveIHOPControl(double ihop_percent,
G1OldGenAllocationTracker const* old_gen_alloc_tracker,
G1Predictions const* predictor,
size_t heap_reserve_percent, // The percentage of total heap capacity that should not be tapped into.
size_t heap_waste_percent); // The percentage of the free space in the heap that we think is not usable for allocation.

virtual size_t get_conc_mark_start_threshold();

virtual void update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size);
virtual void update_allocation_info(double allocation_time_s, size_t additional_buffer_size);
virtual void update_marking_length(double marking_length_s);

virtual void print();
@@ -24,19 +24,39 @@

#include "precompiled.hpp"
#include "gc/g1/g1OldGenAllocationTracker.hpp"
#include "logging/log.hpp"

G1OldGenAllocationTracker::G1OldGenAllocationTracker() :
_last_cycle_old_bytes(0),
_last_cycle_duration(0.0),
_allocated_bytes_since_last_gc(0) {
_last_period_old_gen_bytes(0),
_last_period_old_gen_growth(0),
_humongous_bytes_after_last_gc(0),
_allocated_bytes_since_last_gc(0),
_allocated_humongous_bytes_since_last_gc(0) {
}

void G1OldGenAllocationTracker::reset_after_full_gc() {
_last_cycle_duration = 0;
reset_cycle_after_gc();
}
void G1OldGenAllocationTracker::reset_after_gc(size_t humongous_bytes_after_gc) {
// Calculate actual increase in old, taking eager reclaim into consideration.
size_t last_period_humongous_increase = 0;
if (humongous_bytes_after_gc > _humongous_bytes_after_last_gc) {
last_period_humongous_increase = humongous_bytes_after_gc - _humongous_bytes_after_last_gc;
assert(last_period_humongous_increase <= _allocated_humongous_bytes_since_last_gc,
"Increase larger than allocated " SIZE_FORMAT " <= " SIZE_FORMAT,
last_period_humongous_increase, _allocated_humongous_bytes_since_last_gc);
}
_last_period_old_gen_growth = _allocated_bytes_since_last_gc + last_period_humongous_increase;

// Calculate and record needed values.
_last_period_old_gen_bytes = _allocated_bytes_since_last_gc + _allocated_humongous_bytes_since_last_gc;
_humongous_bytes_after_last_gc = humongous_bytes_after_gc;

void G1OldGenAllocationTracker::reset_after_young_gc(double allocation_duration_s) {
_last_cycle_duration = allocation_duration_s;
reset_cycle_after_gc();
}
log_debug(gc, alloc, stats)("Old generation allocation in the last mutator period, "
"old gen allocated: " SIZE_FORMAT "B, humongous allocated: " SIZE_FORMAT "B,"
"old gen growth: " SIZE_FORMAT "B.",
_allocated_bytes_since_last_gc,
_allocated_humongous_bytes_since_last_gc,
_last_period_old_gen_growth);

// Reset for next mutator period.
_allocated_bytes_since_last_gc = 0;
_allocated_humongous_bytes_since_last_gc = 0;
}
@@ -28,34 +28,42 @@
#include "gc/g1/heapRegion.hpp"
#include "memory/allocation.hpp"

class G1AdaptiveIHOPControl;

// Track allocation details in the old generation.
class G1OldGenAllocationTracker : public CHeapObj<mtGC> {
// New bytes allocated in old gen between the end of the last GC and
// the end of the GC before that.
size_t _last_cycle_old_bytes;
// The number of seconds between the end of the last GC and
// the end of the GC before that.
double _last_cycle_duration;
// Total number of bytes allocated in the old generaton during
// last mutator period.
size_t _last_period_old_gen_bytes;
// Total growth of the old geneneration for last mutator period,
// taking eager reclaim into consideration.
size_t _last_period_old_gen_growth;

size_t _allocated_bytes_since_last_gc;
// Total size of humongous objects for last gc.
size_t _humongous_bytes_after_last_gc;

void reset_cycle_after_gc() {
_last_cycle_old_bytes = _allocated_bytes_since_last_gc;
_allocated_bytes_since_last_gc = 0;
}
// Non-humongous old generation allocations during last mutator period.
size_t _allocated_bytes_since_last_gc;
// Humongous allocations during last mutator period.
size_t _allocated_humongous_bytes_since_last_gc;

public:
G1OldGenAllocationTracker();
// Add the given number of bytes to the total number of allocated bytes in the old gen.

void add_allocated_bytes_since_last_gc(size_t bytes) { _allocated_bytes_since_last_gc += bytes; }
void add_allocated_humongous_bytes_since_last_gc(size_t bytes) { _allocated_humongous_bytes_since_last_gc += bytes; }

size_t last_cycle_old_bytes() { return _last_cycle_old_bytes; }
// Record a humongous allocation in a collection pause. This allocation
// is accounted to the previous mutator period.
void record_collection_pause_humongous_allocation(size_t bytes) {
_humongous_bytes_after_last_gc += bytes;
}

double last_cycle_duration() { return _last_cycle_duration; }
size_t last_period_old_gen_bytes() const { return _last_period_old_gen_bytes; }
size_t last_period_old_gen_growth() const { return _last_period_old_gen_growth; };

// Reset stats after a collection.
void reset_after_full_gc();
void reset_after_young_gc(double allocation_duration_s);
// Calculates and resets stats after a collection.
void reset_after_gc(size_t humongous_bytes_after_gc);
};

#endif // SHARE_VM_GC_G1_G1OLDGENALLOCATIONTRACKER_HPP
#endif // SHARE_VM_GC_G1_G1OLDGENALLOCATIONTRACKER_HPP
@@ -57,7 +57,8 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) :
_analytics(new G1Analytics(&_predictor)),
_remset_tracker(),
_mmu_tracker(new G1MMUTrackerQueue(GCPauseIntervalMillis / 1000.0, MaxGCPauseMillis / 1000.0)),
_ihop_control(create_ihop_control(&_predictor)),
_old_gen_alloc_tracker(),
_ihop_control(create_ihop_control(&_old_gen_alloc_tracker, &_predictor)),
_policy_counters(new GCPolicyCounters("GarbageFirst", 1, 2)),
_full_collection_start_sec(0.0),
_young_list_target_length(0),
@@ -72,7 +73,6 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) :
_rs_length(0),
_rs_length_prediction(0),
_pending_cards_at_gc_start(0),
_old_gen_alloc_tracker(),
_concurrent_start_to_mixed(),
_collection_set(NULL),
_g1h(NULL),
@@ -469,7 +469,7 @@ void G1Policy::record_full_collection_end() {
update_young_list_max_and_target_length();
update_rs_length_prediction();

_old_gen_alloc_tracker.reset_after_full_gc();
_old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * HeapRegion::GrainBytes);

record_pause(FullGC, _full_collection_start_sec, end_sec);
}
@@ -804,9 +804,8 @@ void G1Policy::record_collection_pause_end(double pause_time_ms) {
// predicted target occupancy.
size_t last_unrestrained_young_length = update_young_list_max_and_target_length();

_old_gen_alloc_tracker.reset_after_young_gc(app_time_ms / 1000.0);
update_ihop_prediction(_old_gen_alloc_tracker.last_cycle_duration(),
_old_gen_alloc_tracker.last_cycle_old_bytes(),
_old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * HeapRegion::GrainBytes);
update_ihop_prediction(app_time_ms / 1000.0,
last_unrestrained_young_length * HeapRegion::GrainBytes,
is_young_only_pause(this_pause));

@@ -844,19 +843,20 @@ void G1Policy::record_collection_pause_end(double pause_time_ms) {
scan_logged_cards_time_goal_ms);
}

G1IHOPControl* G1Policy::create_ihop_control(const G1Predictions* predictor){
G1IHOPControl* G1Policy::create_ihop_control(const G1OldGenAllocationTracker* old_gen_alloc_tracker,
const G1Predictions* predictor) {
if (G1UseAdaptiveIHOP) {
return new G1AdaptiveIHOPControl(InitiatingHeapOccupancyPercent,
old_gen_alloc_tracker,
predictor,
G1ReservePercent,
G1HeapWastePercent);
} else {
return new G1StaticIHOPControl(InitiatingHeapOccupancyPercent);
return new G1StaticIHOPControl(InitiatingHeapOccupancyPercent, old_gen_alloc_tracker);
}
}

void G1Policy::update_ihop_prediction(double mutator_time_s,
size_t mutator_alloc_bytes,
size_t young_gen_size,
bool this_gc_was_young_only) {
// Always try to update IHOP prediction. Even evacuation failures give information
@@ -885,7 +885,7 @@ void G1Policy::update_ihop_prediction(double mutator_time_s,
// marking, which makes any prediction useless. This increases the accuracy of the
// prediction.
if (this_gc_was_young_only && mutator_time_s > min_valid_time) {
_ihop_control->update_allocation_info(mutator_time_s, mutator_alloc_bytes, young_gen_size);
_ihop_control->update_allocation_info(mutator_time_s, young_gen_size);
report = true;
}

0 comments on commit 951b12a

Please sign in to comment.