Skip to content

Commit

Permalink
FEAT(gc): reclaim unused internal and Java heap memory when vm is idle
Browse files Browse the repository at this point in the history
add a new thread and check vm load every 5 minutes, when load is below
thresold times, will trigger full gc to reclaim physical memory, the
feature is disable by default.

--story=857362553

Contributed-by:wattsun
  • Loading branch information
wattsun110 authored and linzang committed Nov 6, 2020
1 parent 719857d commit 11d2be1
Show file tree
Hide file tree
Showing 14 changed files with 452 additions and 23 deletions.
12 changes: 5 additions & 7 deletions hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1935,12 +1935,6 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) :

int n_queues = MAX2((int)ParallelGCThreads, 1);
_task_queues = new RefToScanQueueSet(n_queues);
if (FreeHeapPhysicalMemory) {
_free_heap_memory_task_queue = new FreeHeapMemoryTaskQueue();
if (0 != _free_heap_memory_task_queue) {
_free_heap_memory_task_queue->initialize();
}
}

uint n_rem_sets = HeapRegionRemSet::num_par_rem_sets();
assert(n_rem_sets > 0, "Invariant.");
Expand Down Expand Up @@ -4288,6 +4282,11 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
// The "used" of the the collection set have already been subtracted
// when they were freed. Add in the bytes evacuated.
_allocator->increase_used(g1_policy()->bytes_copied_during_gc());

if (PeriodicGCInterval > 0) {
//if no evacuation failure occured, update young gc frequency directly
update_minor_gc_frequency_histogram();
}
}

if (g1_policy()->during_initial_mark_pause()) {
Expand Down Expand Up @@ -7082,7 +7081,6 @@ void G1CollectedHeap::rebuild_strong_code_roots() {

void G1CollectedHeap::print_heap_physical_memory_free_info() {
if (PrintGCDetails) {
gclog_or_tty->print_cr("");
gclog_or_tty->gclog_stamp(GCId::peek());
gclog_or_tty->print_cr(" [free physical memory: " SIZE_FORMAT " KB, " SIZE_FORMAT " regions, %3.7f secs]",\
_free_heap_physical_memory_total_byte_size / K,_reclaim_region_count,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,11 @@ bool PSScavenge::invoke_no_policy() {
heap->gc_policy_counters()->update_counters();

heap->resize_all_tlabs();

if (PeriodicGCInterval > 0) {
//update young gc frequency info
heap->update_minor_gc_frequency_histogram();
}

assert(young_gen->to_space()->is_empty(), "to space should be empty now");
}
Expand Down
95 changes: 95 additions & 0 deletions hotspot/src/share/vm/gc_interface/collectedHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,31 @@ CollectedHeap::CollectedHeap() : _n_par_threads(0)
_gc_cause = _gc_lastcause = GCCause::_no_gc;
NOT_PRODUCT(_promotion_failure_alot_count = 0;)
NOT_PRODUCT(_promotion_failure_alot_gc_number = 0;)
if (FreeHeapPhysicalMemory) {
_free_heap_memory_task_queue = new FreeHeapMemoryTaskQueue();
if (NULL != _free_heap_memory_task_queue) {
_free_heap_memory_task_queue->initialize();
}
_last_minor_gc_time = 0;
// init histogram
if (PeriodicGCInterval > 0) {
_minor_gc_frequency_histogram = new Histogram();
if (NULL == _minor_gc_frequency_histogram) {
// will disable period gc
PeriodicGCInterval = 0;
}
} else {
_minor_gc_frequency_histogram = NULL;
PeriodicGCInterval = 0;
}
_minor_gc_frequency_below_thresold_count = 0;
_last_full_gc_time = 0;
} else {
_minor_gc_frequency_histogram = NULL;
PeriodicGCInterval = 0;
}
guarantee((PeriodicGCInterval > 0 && NULL != _minor_gc_frequency_histogram)
|| (0 == PeriodicGCInterval && NULL == _minor_gc_frequency_histogram), "sanity check");

if (UsePerfData) {
EXCEPTION_MARK;
Expand Down Expand Up @@ -582,6 +607,71 @@ void CollectedHeap::post_full_gc_dump(GCTimer* timer) {
}
}

void CollectedHeap::check_for_periodic_gc(int interval) {
if (should_start_periodic_gc(interval)) {
MutexLockerEx x(FreeHeapMemory_lock);
PeriodicGCTask* task = new PeriodicGCTask(this);
free_heap_memory_task_queue()->push(task);
FreeHeapMemory_lock->notify();
}
}

void CollectedHeap::update_minor_gc_frequency_histogram() {
if (_minor_gc_frequency_histogram) {
if (0 == _last_minor_gc_time) {
_last_minor_gc_time = os::javaTimeMillis();
}else {
size_t now = os::javaTimeMillis();
_minor_gc_frequency_histogram->add(now - _last_minor_gc_time);
_last_minor_gc_time = now;
}
}
}

bool CollectedHeap::should_start_periodic_gc(int interval) {
// disable free physical memory when the system is idle
if (0 == PeriodicGCInterval) {
return false;
}

double now = os::elapsedTime();
// should be static
static int last_check_minor_gc_count = _total_collections - _total_full_collections;
static double last_check_time = now;

// Check if this process load is below thresold.
int minor_gc_count = _total_collections - _total_full_collections;
double minor_gc_frequency = 0.0;
if (minor_gc_count > last_check_minor_gc_count) {
minor_gc_frequency =
(now - last_check_time) / (minor_gc_count - last_check_minor_gc_count);
if (minor_gc_frequency > _minor_gc_frequency_histogram->percentile(99)) {
++_minor_gc_frequency_below_thresold_count;
} else {
// reset it
_minor_gc_frequency_below_thresold_count = 0;
}
last_check_minor_gc_count = minor_gc_count;
} else if ((int)((now - _last_minor_gc_time) * 1000) > interval) {
++_minor_gc_frequency_below_thresold_count;
} else {
// the interval is too short
_minor_gc_frequency_below_thresold_count = 0;
}

// update checktime
last_check_time = now;

// Check if enough time has passed since the last GC.
if ((long)(((now - _last_full_gc_time) * 1000) > PeriodicGCInterval)
&& (_minor_gc_frequency_below_thresold_count >= PeriodicGCLoadThreshold)) {
_minor_gc_frequency_below_thresold_count = 0;
return true;
}

return false;
}

void CollectedHeap::free_heap_physical_memory_after_fullgc(void* start, void* end) {
double start_sec = os::elapsedTime();
//UseLargePages
Expand All @@ -595,6 +685,7 @@ void CollectedHeap::free_heap_physical_memory_after_fullgc(void* start, void* e
os::free_heap_physical_memory(start_address, length);
double end_sec = os::elapsedTime();
_free_heap_physical_memory_time_sec = end_sec - start_sec;
_last_full_gc_time = end_sec;

return;
}
Expand All @@ -610,6 +701,10 @@ void CollectedHeap::print_heap_physical_memory_free_info() {
_free_heap_physical_memory_total_byte_size = 0;
}

void PeriodicGCTask::doit() {
Universe::heap()->collect(GCCause::_periodic_collection);
}

/////////////// Unit tests ///////////////

#ifndef PRODUCT
Expand Down
33 changes: 33 additions & 0 deletions hotspot/src/share/vm/gc_interface/collectedHeap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "runtime/safepoint.hpp"
#include "utilities/events.hpp"
#include "utilities/taskqueue.hpp"
#include "services/histogram.hpp"

// A "CollectedHeap" is an implementation of a java heap for HotSpot. This
// is an abstract class: there may be many different kinds of heaps. This
Expand Down Expand Up @@ -123,6 +124,17 @@ class CollectedHeap : public CHeapObj<mtInternal> {
size_t _free_heap_physical_memory_total_byte_size;
// queue for free heap memory thread
FreeHeapMemoryTaskQueue* _free_heap_memory_task_queue;
//unit is ms
//_last_minor_gc_time will be changed frequently and need to be visible to 2 threads
volatile size_t _last_minor_gc_time;
//histogram for young gc
Histogram* _minor_gc_frequency_histogram;
//last full gc time
//_last_full_gc_time will be changed frequently and need to be visible to 2 threads
volatile double _last_full_gc_time;

//the number of times the young gc frequency is below given threshold
int _minor_gc_frequency_below_thresold_count;

// Constructor
CollectedHeap();
Expand Down Expand Up @@ -322,11 +334,23 @@ class CollectedHeap : public CHeapObj<mtInternal> {
uint n_par_threads() { return _n_par_threads; }

void free_heap_physical_memory_after_fullgc(void* start, void* end);

virtual void print_heap_physical_memory_free_info();

FreeHeapMemoryTaskQueue* free_heap_memory_task_queue() {
return _free_heap_memory_task_queue;
}

Histogram* minor_gc_frequency_histogram() {
return _minor_gc_frequency_histogram;
}

bool should_start_periodic_gc(int);

void check_for_periodic_gc(int);

void update_minor_gc_frequency_histogram();

// May be overridden to set additional parallelism.
virtual void set_par_threads(uint t) { _n_par_threads = t; };

Expand Down Expand Up @@ -697,4 +721,13 @@ class Task {
public:
virtual void doit() {};
};

//task that will do periodic gc when process load is below thresold
class PeriodicGCTask : public Task {
private:
CollectedHeap* _ch;
public:
PeriodicGCTask(CollectedHeap* ch) {_ch = ch;}
virtual void doit();
};
#endif // SHARE_VM_GC_INTERFACE_COLLECTEDHEAP_HPP
3 changes: 3 additions & 0 deletions hotspot/src/share/vm/gc_interface/gcCause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ const char* GCCause::to_string(GCCause::Cause cause) {
case _java_lang_system_gc:
return "System.gc()";

case _periodic_collection:
return "PeriodicGC";

case _full_gc_alot:
return "FullGCAlot";

Expand Down
1 change: 1 addition & 0 deletions hotspot/src/share/vm/gc_interface/gcCause.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class GCCause : public AllStatic {
_g1_humongous_allocation,

_last_ditch_collection,
_periodic_collection,
_last_gc_cause
};

Expand Down
6 changes: 6 additions & 0 deletions hotspot/src/share/vm/memory/genCollectedHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,12 @@ void GenCollectedHeap::do_collection(bool full,
if (complete) { // We did a "major" collection
// FIXME: See comment at pre_full_gc_dump call
post_full_gc_dump(NULL); // do any post full gc dumps
}else {
//update young gc info
//should call this here for ParNew/defNew and tenuredGeneration
if (PeriodicGCInterval > 0) {
update_minor_gc_frequency_histogram();
}
}

if (PrintGCDetails) {
Expand Down
11 changes: 9 additions & 2 deletions hotspot/src/share/vm/runtime/globals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3260,9 +3260,16 @@ class CommandLineFlags {
product(bool, FreeHeapPhysicalMemory, false, \
"Free physical memory after fullgc or shrink operation") \
\
product(intx, PeriodicGCLoadThreshold, 3, \
"when PeriodicGCInterval > 0,jvm will check minor gc frequency"\
"each 5 minutes, and if the frequency in this 5 minutes is" \
"lower than the p99 value for PeriodicGCLoadThreshold" \
"consecutive times, will trigger periodical gc.") \
\
product(uintx, PeriodicGCInterval, 0, \
"Trigger periodic gc when this process is idle, disabled" \
" by default") \
"Number of milliseconds after previous GC to wait before " \
"triggering a periodic gc. A value of zero disables periodically "\
"enforced gc cycles.") \
\
product(intx, SoftRefLRUPolicyMSPerMB, 1000, \
"Number of milliseconds per MB of free space in the heap") \
Expand Down
2 changes: 1 addition & 1 deletion hotspot/src/share/vm/runtime/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3708,7 +3708,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
// if UseG1GC or other gc and periord gc enabled
// we should start new thread
if (FreeHeapPhysicalMemory && (UseG1GC || PeriodicGCInterval != 0)) {
FreeHeapPhysicalMemoryThread::start(SharedHeap::heap());
FreeHeapPhysicalMemoryThread::start();
}
}

Expand Down
19 changes: 9 additions & 10 deletions hotspot/src/share/vm/services/freeHeapMemoryThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@
#include "freeHeapMemoryThread.hpp"
#include "runtime/mutexLocker.hpp"
FreeHeapPhysicalMemoryThread* FreeHeapPhysicalMemoryThread::_thread = 0;
FreeHeapPhysicalMemoryThread::FreeHeapPhysicalMemoryThread(SharedHeap* sh) :
FreeHeapPhysicalMemoryThread::FreeHeapPhysicalMemoryThread() :
ConcurrentGCThread() {
_monitor = FreeHeapMemory_lock;
_sh = sh;
_sh = Universe::heap();
set_name("Free Heap Physical Memory Thread");
create_and_start();
}

void FreeHeapPhysicalMemoryThread::start(SharedHeap* sh) {
_thread = new FreeHeapPhysicalMemoryThread(sh);
void FreeHeapPhysicalMemoryThread::start() {
_thread = new FreeHeapPhysicalMemoryThread();
if (NULL == _thread) {
if (UseG1GC) {
//disable free heap physical memory feature
Expand All @@ -50,11 +50,11 @@ void FreeHeapPhysicalMemoryThread::sleep_before_next_cycle(uintx waitms) {
}

void FreeHeapPhysicalMemoryThread::run() {
int i = 0;
Task* task = 0;
uintx waitms = 1800000; // 1800s
const uintx waitms = 300000; // 5min
while (!_should_terminate) {
sleep_before_next_cycle(waitms);

while (!_sh->free_heap_memory_task_queue()->is_empty()) {
if (_sh->free_heap_memory_task_queue()->pop_local(task)) {
task->doit();
Expand All @@ -63,10 +63,9 @@ void FreeHeapPhysicalMemoryThread::run() {
task = 0;
}
}
//The fullgc is triggered for the first time after the process starts for 1800s, and then for 3600s
if (waitms == 1800000) {
waitms = 3600000;
}

//check if should do periodic gc
_sh->check_for_periodic_gc(waitms);
}
}

Expand Down
6 changes: 3 additions & 3 deletions hotspot/src/share/vm/services/freeHeapMemoryThread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@
class FreeHeapPhysicalMemoryThread: public ConcurrentGCThread {
private:
Monitor* _monitor;
SharedHeap* _sh;
CollectedHeap* _sh;
static FreeHeapPhysicalMemoryThread* _thread;

void stop_service();

void sleep_before_next_cycle(uintx waitms);
FreeHeapPhysicalMemoryThread(SharedHeap* sh);
FreeHeapPhysicalMemoryThread();

public:
virtual void run();
static void start(SharedHeap* sh);
static void start();
static FreeHeapPhysicalMemoryThread* thread() { return _thread;}
};
#endif // FREE_HEAP_PHYSICAL_MEMORY_HPP
Loading

0 comments on commit 11d2be1

Please sign in to comment.