Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8276670: G1: Move and rename G1CardSetFreePool and related classes #6289

Closed
@@ -23,7 +23,7 @@
*/

#include "precompiled.hpp"
#include "gc/g1/g1CardSetFreeMemoryTask.hpp"
#include "gc/g1/g1BufferListFreeMemoryTask.hpp"
#include "gc/g1/g1CardSetMemory.inline.hpp"
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/g1/g1_globals.hpp"
@@ -32,13 +32,13 @@
#include "heapRegionRemSet.hpp"
#include "ci/ciUtilities.hpp"

constexpr const char* G1CardSetFreeMemoryTask::_state_names[];
constexpr const char* G1BufferListFreeMemoryTask::_state_names[];

const char* G1CardSetFreeMemoryTask::get_state_name(State value) const {
const char* G1BufferListFreeMemoryTask::get_state_name(State value) const {
return _state_names[static_cast<std::underlying_type_t<State>>(value)];
}

bool G1CardSetFreeMemoryTask::deadline_exceeded(jlong deadline) {
bool G1BufferListFreeMemoryTask::deadline_exceeded(jlong deadline) {
return os::elapsed_counter() >= deadline;
}

@@ -47,11 +47,11 @@ static size_t keep_size(size_t free, size_t used, double percent) {
return MIN2(free, to_keep);
}

bool G1CardSetFreeMemoryTask::calculate_return_infos(jlong deadline) {
bool G1BufferListFreeMemoryTask::calculate_return_infos(jlong deadline) {
// Ignore the deadline in this step as it is very short.

G1CardSetMemoryStats used = _total_used;
G1CardSetMemoryStats free = G1CardSetFreePool::free_list_sizes();
G1BufferListMemoryStats used = _total_used;
G1BufferListMemoryStats free = G1BufferListFreePool<mtGCCardSet>::free_list_sizes();

_return_info = new G1ReturnMemoryProcessorSet(used.num_pools());
for (uint i = 0; i < used.num_pools(); i++) {
@@ -67,11 +67,11 @@ bool G1CardSetFreeMemoryTask::calculate_return_infos(jlong deadline) {
_return_info->append(new G1ReturnMemoryProcessor(return_to_vm_size));
}

G1CardSetFreePool::update_unlink_processors(_return_info);
G1BufferListFreePool<mtGCCardSet>::update_unlink_processors(_return_info);
return false;
}

bool G1CardSetFreeMemoryTask::return_memory_to_vm(jlong deadline) {
bool G1BufferListFreeMemoryTask::return_memory_to_vm(jlong deadline) {
for (int i = 0; i < _return_info->length(); i++) {
G1ReturnMemoryProcessor* info = _return_info->at(i);
if (!info->finished_return_to_vm()) {
@@ -83,7 +83,7 @@ bool G1CardSetFreeMemoryTask::return_memory_to_vm(jlong deadline) {
return false;
}

bool G1CardSetFreeMemoryTask::return_memory_to_os(jlong deadline) {
bool G1BufferListFreeMemoryTask::return_memory_to_os(jlong deadline) {
for (int i = 0; i < _return_info->length(); i++) {
G1ReturnMemoryProcessor* info = _return_info->at(i);
if (!info->finished_return_to_os()) {
@@ -95,7 +95,7 @@ bool G1CardSetFreeMemoryTask::return_memory_to_os(jlong deadline) {
return false;
}

bool G1CardSetFreeMemoryTask::cleanup_return_infos() {
bool G1BufferListFreeMemoryTask::cleanup_return_infos() {
for (int i = 0; i < _return_info->length(); i++) {
G1ReturnMemoryProcessor* info = _return_info->at(i);
delete info;
@@ -106,7 +106,7 @@ bool G1CardSetFreeMemoryTask::cleanup_return_infos() {
return false;
}

bool G1CardSetFreeMemoryTask::free_excess_card_set_memory() {
bool G1BufferListFreeMemoryTask::free_excess_card_set_memory() {
jlong start = os::elapsed_counter();
jlong end = start +
(os::elapsed_frequency() / 1000) * G1RemSetFreeMemoryStepDurationMillis;
@@ -163,34 +163,34 @@ bool G1CardSetFreeMemoryTask::free_excess_card_set_memory() {
return is_active();
}

void G1CardSetFreeMemoryTask::set_state(State new_state) {
void G1BufferListFreeMemoryTask::set_state(State new_state) {
log_trace(gc, task)("Card Set Free Memory: State change from %s to %s",
get_state_name(_state),
get_state_name(new_state));
_state = new_state;
}

bool G1CardSetFreeMemoryTask::is_active() const {
bool G1BufferListFreeMemoryTask::is_active() const {
return _state != State::Inactive;
}

jlong G1CardSetFreeMemoryTask::reschedule_delay_ms() const {
jlong G1BufferListFreeMemoryTask::reschedule_delay_ms() const {
return G1RemSetFreeMemoryRescheduleDelayMillis;
}

G1CardSetFreeMemoryTask::G1CardSetFreeMemoryTask(const char* name) :
G1BufferListFreeMemoryTask::G1BufferListFreeMemoryTask(const char* name) :
G1ServiceTask(name), _state(State::CalculateUsed), _return_info(nullptr) { }

void G1CardSetFreeMemoryTask::execute() {
void G1BufferListFreeMemoryTask::execute() {
SuspendibleThreadSetJoiner sts;

if (free_excess_card_set_memory()) {
schedule(reschedule_delay_ms());
}
}

void G1CardSetFreeMemoryTask::notify_new_stats(G1CardSetMemoryStats* young_gen_stats,
G1CardSetMemoryStats* collection_set_candidate_stats) {
void G1BufferListFreeMemoryTask::notify_new_stats(G1BufferListMemoryStats* young_gen_stats,
G1BufferListMemoryStats* collection_set_candidate_stats) {
assert_at_safepoint_on_vm_thread();

_total_used = *young_gen_stats;
@@ -22,17 +22,18 @@
*
*/

#ifndef SHARE_GC_G1_G1CARDSETFREEMEMORYTASK_HPP
#define SHARE_GC_G1_G1CARDSETFREEMEMORYTASK_HPP
#ifndef SHARE_GC_G1_G1BUFFERLISTFREEMEMORYTASK_HPP
#define SHARE_GC_G1_G1BUFFERLISTFREEMEMORYTASK_HPP

#include "gc/g1/g1ServiceThread.hpp"
#include "gc/g1/g1BufferListFreePool.hpp"
#include "gc/g1/g1CardSetMemory.hpp"
#include "gc/g1/heapRegionRemSet.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/ticks.hpp"

// Task handling deallocation of free card set memory.
class G1CardSetFreeMemoryTask : public G1ServiceTask {
class G1BufferListFreeMemoryTask : public G1ServiceTask {

enum class State : uint {
Inactive,
@@ -53,10 +54,10 @@ class G1CardSetFreeMemoryTask : public G1ServiceTask {
State _state;

// Current total card set memory usage.
G1CardSetMemoryStats _total_used;
G1BufferListMemoryStats _total_used;
Copy link
Contributor

@tschatzl tschatzl Nov 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment mentions "card set"


typedef G1CardSetFreePool::G1ReturnMemoryProcessor G1ReturnMemoryProcessor;
typedef G1CardSetFreePool::G1ReturnMemoryProcessorSet G1ReturnMemoryProcessorSet;
typedef G1BufferListFreePool<mtGCCardSet>::G1ReturnMemoryProcessor G1ReturnMemoryProcessor;
typedef G1BufferListFreePool<mtGCCardSet>::G1ReturnMemoryProcessorSet G1ReturnMemoryProcessorSet;

G1ReturnMemoryProcessorSet* _return_info;

@@ -82,14 +83,14 @@ class G1CardSetFreeMemoryTask : public G1ServiceTask {
jlong reschedule_delay_ms() const;

public:
explicit G1CardSetFreeMemoryTask(const char* name);
explicit G1BufferListFreeMemoryTask(const char* name);

void execute() override;

// Notify the task of new used remembered set memory statistics for the young
// generation and the collection set candidate sets.
void notify_new_stats(G1CardSetMemoryStats* young_gen_stats,
G1CardSetMemoryStats* collection_set_candidate_stats);
void notify_new_stats(G1BufferListMemoryStats* young_gen_stats,
G1BufferListMemoryStats* collection_set_candidate_stats);
};

#endif // SHARE_GC_G1_G1CARDSETFREEMEMORYTASK_HPP
#endif // SHARE_GC_G1_G1BUFFERLISTFREEMEMORYTASK_HPP
@@ -0,0 +1,205 @@
/*
* Copyright (c) 2021, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#include "precompiled.hpp"

#include "gc/g1/g1BufferListFreePool.hpp"
#include "gc/g1/g1SegmentedArray.inline.hpp"
#include "logging/log.hpp"
#include "memory/allocation.hpp"
#include "runtime/os.hpp"
#include "utilities/formatBuffer.hpp"
#include "utilities/ostream.hpp"

G1BufferListMemoryStats::G1BufferListMemoryStats() {
clear();
}

void G1BufferListMemoryStats::clear() {
for (uint i = 0; i < num_pools(); i++) {
_num_mem_sizes[i] = 0;
_num_buffers[i] = 0;
}
}

template<MEMFLAGS flag>
void G1BufferListFreePool<flag>::update_unlink_processors(G1ReturnMemoryProcessorSet* unlink_processor) {
uint num_free_lists = _freelist_pool.num_free_lists();

for (uint i = 0; i < num_free_lists; i++) {
unlink_processor->at(i)->visit_free_list(_freelist_pool.free_list(i));
}
}

template<MEMFLAGS flag>
void G1BufferListFreePool<flag>::G1ReturnMemoryProcessor::visit_free_list(G1SegmentedArrayBufferList<flag>* source) {
assert(_source == nullptr, "already visited");
if (_return_to_vm_size > 0) {
_source = source;
} else {
assert(_source == nullptr, "must be");
}
if (source->mem_size() > _return_to_vm_size) {
_first = source->get_all(_num_unlinked, _unlinked_bytes);
} else {
assert(_first == nullptr, "must be");
}
// Above we were racing with other threads getting the contents of the free list,
// so while we might have been asked to return something to the OS initially,
// the free list might be empty anyway. In this case just reset internal values
// used for checking whether there is work available.
if (_first == nullptr) {
_source = nullptr;
_return_to_vm_size = 0;
}
}

template<MEMFLAGS flag>
bool G1BufferListFreePool<flag>::G1ReturnMemoryProcessor::return_to_vm(jlong deadline) {
assert(!finished_return_to_vm(), "already returned everything to the VM");
assert(_first != nullptr, "must have element to return");

size_t keep_size = 0;
size_t keep_num = 0;

G1SegmentedArrayBuffer<flag>* cur = _first;
G1SegmentedArrayBuffer<flag>* last = nullptr;

while (cur != nullptr && _return_to_vm_size > 0) {
size_t cur_size = cur->mem_size();
_return_to_vm_size -= MIN2(_return_to_vm_size, cur_size);

keep_size += cur_size;
keep_num++;

last = cur;
cur = cur->next();
// To ensure progress, perform the deadline check here.
if (os::elapsed_counter() > deadline) {
break;
}
}

assert(_first != nullptr, "must be");
assert(last != nullptr, "must be");

last->set_next(nullptr);

// Wait for any in-progress pops to avoid ABA for them.
GlobalCounter::write_synchronize();
_source->bulk_add(*_first, *last, keep_num, keep_size);
_first = cur;

log_trace(gc, task)("Card Set Free Memory: Returned to VM %zu buffers size %zu", keep_num, keep_size);
Copy link
Contributor

@tschatzl tschatzl Nov 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Card Set"


// _return_to_vm_size may be larger than what is available in the list at the
// time we actually get the list. I.e. the list and _return_to_vm_size may be
// inconsistent.
// So also check if we actually already at the end of the list for the exit
// condition.
if (_return_to_vm_size == 0 || _first == nullptr) {
_source = nullptr;
_return_to_vm_size = 0;
}
return _source != nullptr;
}

template<MEMFLAGS flag>
bool G1BufferListFreePool<flag>::G1ReturnMemoryProcessor::return_to_os(jlong deadline) {
assert(finished_return_to_vm(), "not finished returning to VM");
assert(!finished_return_to_os(), "already returned everything to the OS");

// Now delete the rest.
size_t num_delete = 0;
size_t mem_size_deleted = 0;

while (_first != nullptr) {
G1SegmentedArrayBuffer<flag>* next = _first->next();
num_delete++;
mem_size_deleted += _first->mem_size();
delete _first;
_first = next;

// To ensure progress, perform the deadline check here.
if (os::elapsed_counter() > deadline) {
break;
}
}

log_trace(gc, task)("Card Set Free Memory: Return to OS %zu buffers size %zu", num_delete, mem_size_deleted);

return _first != nullptr;
}

template<MEMFLAGS flag>
G1BufferListFreePool<flag> G1BufferListFreePool<flag>::_freelist_pool(G1CardSetConfiguration::num_mem_object_types());

template<MEMFLAGS flag>
G1BufferListFreePool<flag>::G1BufferListFreePool(uint num_free_lists) :
_num_free_lists(num_free_lists) {

_free_lists = NEW_C_HEAP_ARRAY(G1SegmentedArrayBufferList<flag>, _num_free_lists, mtGC);
for (uint i = 0; i < _num_free_lists; i++) {
new (&_free_lists[i]) G1SegmentedArrayBufferList<flag>();
}
}

template<MEMFLAGS flag>
G1BufferListFreePool<flag>::~G1BufferListFreePool() {
for (uint i = 0; i < _num_free_lists; i++) {
_free_lists[i].~G1SegmentedArrayBufferList<flag>();
}
FREE_C_HEAP_ARRAY(mtGC, _free_lists);
}

template<MEMFLAGS flag>
G1BufferListMemoryStats G1BufferListFreePool<flag>::memory_sizes() const {
G1BufferListMemoryStats free_list_stats;
assert(free_list_stats.num_pools() == num_free_lists(), "must be");
for (uint i = 0; i < num_free_lists(); i++) {
free_list_stats._num_mem_sizes[i] = _free_lists[i].mem_size();
free_list_stats._num_buffers[i] = _free_lists[i].num_buffers();
}
return free_list_stats;
}

template<MEMFLAGS flag>
size_t G1BufferListFreePool<flag>::mem_size() const {
size_t result = 0;
for (uint i = 0; i < _num_free_lists; i++) {
result += _free_lists[i].mem_size();
}
return result;
}

template<MEMFLAGS flag>
void G1BufferListFreePool<flag>::print_on(outputStream* out) {
out->print_cr(" Free Pool: size %zu", free_list_pool()->mem_size());
for (uint i = 0; i < _num_free_lists; i++) {
FormatBuffer<> fmt(" %s", G1CardSetConfiguration::mem_object_type_name_str(i));
_free_lists[i].print_on(out, fmt);
}
}

template class G1BufferListFreePool<mtGCCardSet>;