| /* |
| * Copyright (c) 2021, 2022, 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/g1MonotonicArena.inline.hpp" |
| #include "gc/g1/g1MonotonicArenaFreePool.hpp" |
| #include "logging/log.hpp" |
| #include "memory/allocation.hpp" |
| #include "runtime/os.hpp" |
| #include "utilities/formatBuffer.hpp" |
| #include "utilities/ostream.hpp" |
| |
| G1MonotonicArenaMemoryStats::G1MonotonicArenaMemoryStats() { |
| clear(); |
| } |
| |
| void G1MonotonicArenaMemoryStats::clear() { |
| for (uint i = 0; i < num_pools(); i++) { |
| _num_mem_sizes[i] = 0; |
| _num_segments[i] = 0; |
| } |
| } |
| |
| void G1MonotonicArenaFreePool::update_unlink_processors(G1ReturnMemoryProcessorSet* unlink_processor) { |
| |
| for (uint i = 0; i < num_free_lists(); i++) { |
| unlink_processor->at(i)->visit_free_list(free_list(i)); |
| } |
| } |
| |
| void G1MonotonicArenaFreePool::G1ReturnMemoryProcessor::visit_free_list(G1MonotonicArena::SegmentFreeList* 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; |
| } |
| } |
| |
| bool G1MonotonicArenaFreePool::G1ReturnMemoryProcessor::return_to_vm(jlong deadline) { |
| assert(!finished_return_to_vm(), "already returned everything to the VM"); |
| assert(_first != nullptr, "must have segment to return"); |
| |
| size_t keep_size = 0; |
| size_t keep_num = 0; |
| |
| Segment* cur = _first; |
| Segment* 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)("Monotonic Arena Free Memory: Returned to VM %zu segments size %zu", keep_num, keep_size); |
| |
| // _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; |
| } |
| |
| bool G1MonotonicArenaFreePool::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) { |
| Segment* next = _first->next(); |
| num_delete++; |
| mem_size_deleted += _first->mem_size(); |
| Segment::delete_segment(_first); |
| _first = next; |
| |
| // To ensure progress, perform the deadline check here. |
| if (os::elapsed_counter() > deadline) { |
| break; |
| } |
| } |
| |
| log_trace(gc, task)("Monotonic Arena Free Memory: Return to OS %zu segments size %zu", num_delete, mem_size_deleted); |
| |
| return _first != nullptr; |
| } |
| |
| G1MonotonicArenaFreePool::G1MonotonicArenaFreePool(uint num_free_lists) : |
| _num_free_lists(num_free_lists) { |
| |
| _free_lists = NEW_C_HEAP_ARRAY(SegmentFreeList, _num_free_lists, mtGC); |
| for (uint i = 0; i < _num_free_lists; i++) { |
| new (&_free_lists[i]) SegmentFreeList(); |
| } |
| } |
| |
| G1MonotonicArenaFreePool::~G1MonotonicArenaFreePool() { |
| for (uint i = 0; i < _num_free_lists; i++) { |
| _free_lists[i].~SegmentFreeList(); |
| } |
| FREE_C_HEAP_ARRAY(mtGC, _free_lists); |
| } |
| |
| G1MonotonicArenaMemoryStats G1MonotonicArenaFreePool::memory_sizes() const { |
| G1MonotonicArenaMemoryStats 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_segments[i] = _free_lists[i].num_segments(); |
| } |
| return free_list_stats; |
| } |
| |
| size_t G1MonotonicArenaFreePool::mem_size() const { |
| size_t result = 0; |
| for (uint i = 0; i < _num_free_lists; i++) { |
| result += _free_lists[i].mem_size(); |
| } |
| return result; |
| } |
| |
| void G1MonotonicArenaFreePool::print_on(outputStream* out) const { |
| out->print_cr(" Free Pool: size %zu", 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); |
| } |
| } |