| /* | |
| * Copyright (c) 2001, 2025, 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 "gc/parallel/objectStartArray.inline.hpp" | |
| #include "gc/shared/cardTableBarrierSet.hpp" | |
| #include "memory/memoryReserver.hpp" | |
| #include "oops/oop.inline.hpp" | |
| #include "runtime/java.hpp" | |
| #include "utilities/align.hpp" | |
| static size_t num_bytes_required(MemRegion mr) { | |
| assert(CardTable::is_card_aligned(mr.start()), "precondition"); | |
| assert(CardTable::is_card_aligned(mr.end()), "precondition"); | |
| return mr.word_size() / CardTable::card_size_in_words(); | |
| } | |
| ObjectStartArray::ObjectStartArray(MemRegion covered_region) | |
| : _virtual_space(nullptr) { | |
| // Calculate how much space must be reserved | |
| size_t bytes_to_reserve = num_bytes_required(covered_region); | |
| assert(bytes_to_reserve > 0, "Sanity"); | |
| bytes_to_reserve = | |
| align_up(bytes_to_reserve, os::vm_allocation_granularity()); | |
| // Do not use large-pages for the backing store. The one large page region | |
| // will be used for the heap proper. | |
| ReservedSpace backing_store = MemoryReserver::reserve(bytes_to_reserve, mtGC); | |
| if (!backing_store.is_reserved()) { | |
| vm_exit_during_initialization("Could not reserve space for ObjectStartArray"); | |
| } | |
| // We do not commit any memory initially | |
| _virtual_space = new PSVirtualSpace(backing_store, os::vm_page_size()); | |
| assert(_virtual_space->low_boundary() != nullptr, "set from the backing_store"); | |
| _offset_base = (uint8_t*)(_virtual_space->low_boundary() - (uintptr_t(covered_region.start()) >> CardTable::card_shift())); | |
| } | |
| void ObjectStartArray::set_covered_region(MemRegion mr) { | |
| DEBUG_ONLY(_covered_region = mr;) | |
| size_t requested_size = num_bytes_required(mr); | |
| // Only commit memory in page-sized chunks | |
| requested_size = align_up(requested_size, os::vm_page_size()); | |
| size_t current_size = _virtual_space->committed_size(); | |
| if (requested_size == current_size) { | |
| return; | |
| } | |
| if (requested_size > current_size) { | |
| // Expand | |
| size_t expand_by = requested_size - current_size; | |
| if (!_virtual_space->expand_by(expand_by)) { | |
| vm_exit_out_of_memory(expand_by, OOM_MMAP_ERROR, "object start array expansion"); | |
| } | |
| } else { | |
| // Shrink | |
| size_t shrink_by = current_size - requested_size; | |
| _virtual_space->shrink_by(shrink_by); | |
| } | |
| } | |
| static void fill_range(uint8_t* start, uint8_t* end, uint8_t v) { | |
| // + 1 for inclusive | |
| memset(start, v, pointer_delta(end, start, sizeof(uint8_t)) + 1); | |
| } | |
| void ObjectStartArray::update_for_block_work(HeapWord* blk_start, | |
| HeapWord* blk_end) { | |
| HeapWord* const cur_card_boundary = align_up_by_card_size(blk_start); | |
| uint8_t* const offset_entry = entry_for_addr(cur_card_boundary); | |
| // The first card holds the actual offset. | |
| *offset_entry = checked_cast<uint8_t>(pointer_delta(cur_card_boundary, blk_start)); | |
| // Check if this block spans over other cards. | |
| uint8_t* const end_entry = entry_for_addr(blk_end - 1); | |
| assert(offset_entry <= end_entry, "inv"); | |
| if (offset_entry != end_entry) { | |
| // Handling remaining entries. | |
| uint8_t* start_entry_for_region = offset_entry + 1; | |
| for (uint i = 0; i < BOTConstants::N_powers; i++) { | |
| // -1 so that the reach ends in this region and not at the start | |
| // of the next. | |
| uint8_t* reach = offset_entry + BOTConstants::power_to_cards_back(i + 1) - 1; | |
| uint8_t value = checked_cast<uint8_t>(CardTable::card_size_in_words() + i); | |
| fill_range(start_entry_for_region, MIN2(reach, end_entry), value); | |
| start_entry_for_region = reach + 1; | |
| if (reach >= end_entry) { | |
| break; | |
| } | |
| } | |
| assert(start_entry_for_region > end_entry, "Sanity check"); | |
| } | |
| DEBUG_ONLY(verify_for_block(blk_start, blk_end);) | |
| } | |
| void ObjectStartArray::verify_for_block(HeapWord* blk_start, HeapWord* blk_end) const { | |
| assert(is_crossing_card_boundary(blk_start, blk_end), "precondition"); | |
| const uint8_t* const start_entry = entry_for_addr(align_up_by_card_size(blk_start)); | |
| const uint8_t* const end_entry = entry_for_addr(blk_end - 1); | |
| // Check entries in [start_entry, end_entry] | |
| assert(*start_entry < CardTable::card_size_in_words(), "offset entry"); | |
| for (const uint8_t* i = start_entry + 1; i <= end_entry; ++i) { | |
| const uint8_t prev = *(i-1); | |
| const uint8_t value = *i; | |
| if (prev != value) { | |
| assert(value >= prev, "monotonic"); | |
| size_t n_cards_back = BOTConstants::entry_to_cards_back(value); | |
| assert(start_entry == (i - n_cards_back), "inv"); | |
| } | |
| } | |
| } |