mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
694 lines
27 KiB
C++
694 lines
27 KiB
C++
/*
|
|
* Copyright (c) 1997, 2024, 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 "jvm.h"
|
|
#include "logging/log.hpp"
|
|
#include "memory/memoryReserver.hpp"
|
|
#include "oops/compressedOops.hpp"
|
|
#include "oops/markWord.hpp"
|
|
#include "runtime/globals_extension.hpp"
|
|
#include "runtime/java.hpp"
|
|
#include "runtime/os.hpp"
|
|
#include "utilities/formatBuffer.hpp"
|
|
#include "utilities/globalDefinitions.hpp"
|
|
#include "utilities/powerOfTwo.hpp"
|
|
|
|
static void sanity_check_size_and_alignment(size_t size, size_t alignment) {
|
|
assert(size > 0, "Precondition");
|
|
|
|
DEBUG_ONLY(const size_t granularity = os::vm_allocation_granularity());
|
|
assert(is_aligned(size, granularity), "size not aligned to os::vm_allocation_granularity()");
|
|
|
|
assert(alignment >= granularity, "Must be set");
|
|
assert(is_power_of_2(alignment), "not a power of 2");
|
|
assert(is_aligned(alignment, granularity), "alignment not aligned to os::vm_allocation_granularity()");
|
|
}
|
|
|
|
static void sanity_check_page_size(size_t page_size) {
|
|
assert(page_size >= os::vm_page_size(), "Invalid page size");
|
|
assert(is_power_of_2(page_size), "Invalid page size");
|
|
}
|
|
|
|
static void sanity_check_arguments(size_t size, size_t alignment, size_t page_size) {
|
|
sanity_check_size_and_alignment(size, alignment);
|
|
sanity_check_page_size(page_size);
|
|
}
|
|
|
|
static bool large_pages_requested() {
|
|
return UseLargePages &&
|
|
(!FLAG_IS_DEFAULT(UseLargePages) || !FLAG_IS_DEFAULT(LargePageSizeInBytes));
|
|
}
|
|
|
|
static void log_on_large_pages_failure(char* req_addr, size_t bytes) {
|
|
if (large_pages_requested()) {
|
|
// Compressed oops logging.
|
|
log_debug(gc, heap, coops)("Reserve regular memory without large pages");
|
|
// JVM style warning that we did not succeed in using large pages.
|
|
char msg[128];
|
|
jio_snprintf(msg, sizeof(msg), "Failed to reserve and commit memory using large pages. "
|
|
"req_addr: " PTR_FORMAT " bytes: " SIZE_FORMAT,
|
|
req_addr, bytes);
|
|
warning("%s", msg);
|
|
}
|
|
}
|
|
|
|
static bool use_explicit_large_pages(size_t page_size) {
|
|
return !os::can_commit_large_page_memory() &&
|
|
page_size != os::vm_page_size();
|
|
}
|
|
|
|
static char* reserve_memory_inner(char* requested_address,
|
|
size_t size,
|
|
size_t alignment,
|
|
bool exec,
|
|
MemTag mem_tag) {
|
|
// If the memory was requested at a particular address, use
|
|
// os::attempt_reserve_memory_at() to avoid mapping over something
|
|
// important. If the reservation fails, return null.
|
|
if (requested_address != nullptr) {
|
|
assert(is_aligned(requested_address, alignment),
|
|
"Requested address " PTR_FORMAT " must be aligned to " SIZE_FORMAT,
|
|
p2i(requested_address), alignment);
|
|
return os::attempt_reserve_memory_at(requested_address, size, exec, mem_tag);
|
|
}
|
|
|
|
// Optimistically assume that the OS returns an aligned base pointer.
|
|
// When reserving a large address range, most OSes seem to align to at
|
|
// least 64K.
|
|
char* base = os::reserve_memory(size, exec, mem_tag);
|
|
if (is_aligned(base, alignment)) {
|
|
return base;
|
|
}
|
|
|
|
// Base not aligned, retry.
|
|
if (!os::release_memory(base, size)) {
|
|
fatal("os::release_memory failed");
|
|
}
|
|
|
|
// Map using the requested alignment.
|
|
return os::reserve_memory_aligned(size, alignment, exec);
|
|
}
|
|
|
|
ReservedSpace MemoryReserver::reserve_memory(char* requested_address,
|
|
size_t size,
|
|
size_t alignment,
|
|
bool exec,
|
|
MemTag mem_tag) {
|
|
char* base = reserve_memory_inner(requested_address, size, alignment, exec, mem_tag);
|
|
|
|
if (base != nullptr) {
|
|
return ReservedSpace(base, size, alignment, os::vm_page_size(), exec, false /* special */);
|
|
}
|
|
|
|
// Failed
|
|
return {};
|
|
}
|
|
|
|
ReservedSpace MemoryReserver::reserve_memory_special(char* requested_address,
|
|
size_t size,
|
|
size_t alignment,
|
|
size_t page_size,
|
|
bool exec) {
|
|
log_trace(pagesize)("Attempt special mapping: size: " SIZE_FORMAT "%s, "
|
|
"alignment: " SIZE_FORMAT "%s",
|
|
byte_size_in_exact_unit(size), exact_unit_for_byte_size(size),
|
|
byte_size_in_exact_unit(alignment), exact_unit_for_byte_size(alignment));
|
|
|
|
char* base = os::reserve_memory_special(size, alignment, page_size, requested_address, exec);
|
|
|
|
if (base != nullptr) {
|
|
assert(is_aligned(base, alignment),
|
|
"reserve_memory_special() returned an unaligned address, "
|
|
"base: " PTR_FORMAT " alignment: " SIZE_FORMAT_X,
|
|
p2i(base), alignment);
|
|
|
|
return ReservedSpace(base, size, alignment, page_size, exec, true /* special */);
|
|
}
|
|
|
|
// Failed
|
|
return {};
|
|
}
|
|
|
|
ReservedSpace MemoryReserver::reserve(char* requested_address,
|
|
size_t size,
|
|
size_t alignment,
|
|
size_t page_size,
|
|
bool executable,
|
|
MemTag mem_tag) {
|
|
sanity_check_arguments(size, alignment, page_size);
|
|
|
|
// Reserve the memory.
|
|
|
|
// There are basically three different cases that we need to handle:
|
|
// 1. Mapping backed by a file
|
|
// 2. Mapping backed by explicit large pages
|
|
// 3. Mapping backed by normal pages or transparent huge pages
|
|
// The first two have restrictions that requires the whole mapping to be
|
|
// committed up front. To record this the ReservedSpace is marked 'special'.
|
|
|
|
// == Case 1 ==
|
|
// This case is contained within the HeapReserver
|
|
|
|
// == Case 2 ==
|
|
if (use_explicit_large_pages(page_size)) {
|
|
// System can't commit large pages i.e. use transparent huge pages and
|
|
// the caller requested large pages. To satisfy this request we use
|
|
// explicit large pages and these have to be committed up front to ensure
|
|
// no reservations are lost.
|
|
do {
|
|
ReservedSpace reserved = reserve_memory_special(requested_address, size, alignment, page_size, executable);
|
|
if (reserved.is_reserved()) {
|
|
// Successful reservation using large pages.
|
|
return reserved;
|
|
}
|
|
page_size = os::page_sizes().next_smaller(page_size);
|
|
} while (page_size > os::vm_page_size());
|
|
|
|
// Failed to reserve explicit large pages, do proper logging.
|
|
log_on_large_pages_failure(requested_address, size);
|
|
// Now fall back to normal reservation.
|
|
assert(page_size == os::vm_page_size(), "inv");
|
|
}
|
|
|
|
// == Case 3 ==
|
|
return reserve_memory(requested_address, size, alignment, executable, mem_tag);
|
|
}
|
|
|
|
ReservedSpace MemoryReserver::reserve(char* requested_address,
|
|
size_t size,
|
|
size_t alignment,
|
|
size_t page_size,
|
|
MemTag mem_tag) {
|
|
return reserve(requested_address,
|
|
size,
|
|
alignment,
|
|
page_size,
|
|
!ExecMem,
|
|
mem_tag);
|
|
}
|
|
|
|
|
|
ReservedSpace MemoryReserver::reserve(size_t size,
|
|
size_t alignment,
|
|
size_t page_size,
|
|
MemTag mem_tag) {
|
|
return reserve(nullptr /* requested_address */,
|
|
size,
|
|
alignment,
|
|
page_size,
|
|
mem_tag);
|
|
}
|
|
|
|
ReservedSpace MemoryReserver::reserve(size_t size,
|
|
MemTag mem_tag) {
|
|
// Want to use large pages where possible. If the size is
|
|
// not large page aligned the mapping will be a mix of
|
|
// large and normal pages.
|
|
size_t page_size = os::page_size_for_region_unaligned(size, 1);
|
|
size_t alignment = os::vm_allocation_granularity();
|
|
|
|
return reserve(size,
|
|
alignment,
|
|
page_size,
|
|
mem_tag);
|
|
}
|
|
|
|
bool MemoryReserver::release(const ReservedSpace& reserved) {
|
|
assert(reserved.is_reserved(), "Precondition");
|
|
|
|
if (reserved.special()) {
|
|
return os::release_memory_special(reserved.base(), reserved.size());
|
|
} else {
|
|
return os::release_memory(reserved.base(), reserved.size());
|
|
}
|
|
}
|
|
|
|
static char* map_memory_to_file(char* requested_address,
|
|
size_t size,
|
|
size_t alignment,
|
|
int fd,
|
|
MemTag mem_tag) {
|
|
// If the memory was requested at a particular address, use
|
|
// os::attempt_reserve_memory_at() to avoid mapping over something
|
|
// important. If the reservation fails, return null.
|
|
if (requested_address != nullptr) {
|
|
assert(is_aligned(requested_address, alignment),
|
|
"Requested address " PTR_FORMAT " must be aligned to " SIZE_FORMAT,
|
|
p2i(requested_address), alignment);
|
|
return os::attempt_map_memory_to_file_at(requested_address, size, fd, mem_tag);
|
|
}
|
|
|
|
// Optimistically assume that the OS returns an aligned base pointer.
|
|
// When reserving a large address range, most OSes seem to align to at
|
|
// least 64K.
|
|
char* base = os::map_memory_to_file(size, fd);
|
|
if (is_aligned(base, alignment)) {
|
|
return base;
|
|
}
|
|
|
|
|
|
// Base not aligned, retry.
|
|
if (!os::unmap_memory(base, size)) {
|
|
fatal("os::unmap_memory failed");
|
|
}
|
|
|
|
// Map using the requested alignment.
|
|
return os::map_memory_to_file_aligned(size, alignment, fd, mem_tag);
|
|
}
|
|
|
|
ReservedSpace FileMappedMemoryReserver::reserve(char* requested_address,
|
|
size_t size,
|
|
size_t alignment,
|
|
int fd,
|
|
MemTag mem_tag) {
|
|
sanity_check_size_and_alignment(size, alignment);
|
|
|
|
char* base = map_memory_to_file(requested_address, size, alignment, fd, mem_tag);
|
|
|
|
if (base != nullptr) {
|
|
return ReservedSpace(base, size, alignment, os::vm_page_size(), !ExecMem, true /* special */);
|
|
}
|
|
|
|
// Failed
|
|
return {};
|
|
}
|
|
|
|
ReservedSpace CodeMemoryReserver::reserve(size_t size,
|
|
size_t alignment,
|
|
size_t page_size) {
|
|
return MemoryReserver::reserve(nullptr /* requested_address */,
|
|
size,
|
|
alignment,
|
|
page_size,
|
|
ExecMem,
|
|
mtCode);
|
|
}
|
|
|
|
ReservedHeapSpace HeapReserver::Instance::reserve_uncompressed_oops_heap(size_t size,
|
|
size_t alignment,
|
|
size_t page_size) {
|
|
ReservedSpace reserved = reserve_memory(size, alignment, page_size);
|
|
|
|
if (reserved.is_reserved()) {
|
|
return ReservedHeapSpace(reserved, 0 /* noaccess_prefix */);
|
|
}
|
|
|
|
// Failed
|
|
return {};
|
|
}
|
|
|
|
|
|
static int maybe_create_file(const char* heap_allocation_directory) {
|
|
if (heap_allocation_directory == nullptr) {
|
|
return -1;
|
|
}
|
|
|
|
int fd = os::create_file_for_heap(heap_allocation_directory);
|
|
if (fd == -1) {
|
|
vm_exit_during_initialization(
|
|
err_msg("Could not create file for Heap at location %s", heap_allocation_directory));
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
HeapReserver::Instance::Instance(const char* heap_allocation_directory)
|
|
: _fd(maybe_create_file(heap_allocation_directory)) {}
|
|
|
|
HeapReserver::Instance::~Instance() {
|
|
if (_fd != -1) {
|
|
::close(_fd);
|
|
}
|
|
}
|
|
|
|
ReservedSpace HeapReserver::Instance::reserve_memory(size_t size,
|
|
size_t alignment,
|
|
size_t page_size,
|
|
char* requested_address) {
|
|
|
|
// There are basically three different cases that we need to handle below:
|
|
// 1. Mapping backed by a file
|
|
// 2. Mapping backed by explicit large pages
|
|
// 3. Mapping backed by normal pages or transparent huge pages
|
|
// The first two have restrictions that requires the whole mapping to be
|
|
// committed up front. To record this the ReservedSpace is marked 'special'.
|
|
|
|
// == Case 1 ==
|
|
if (_fd != -1) {
|
|
// When there is a backing file directory for this space then whether
|
|
// large pages are allocated is up to the filesystem of the backing file.
|
|
// So UseLargePages is not taken into account for this reservation.
|
|
//
|
|
// If requested, let the user know that explicit large pages can't be used.
|
|
if (use_explicit_large_pages(page_size) && large_pages_requested()) {
|
|
log_debug(gc, heap)("Cannot allocate explicit large pages for Java Heap when AllocateHeapAt option is set.");
|
|
}
|
|
|
|
// Always return, not possible to fall back to reservation not using a file.
|
|
return FileMappedMemoryReserver::reserve(requested_address, size, alignment, _fd, mtJavaHeap);
|
|
}
|
|
|
|
// == Case 2 & 3 ==
|
|
return MemoryReserver::reserve(requested_address, size, alignment, page_size, mtJavaHeap);
|
|
}
|
|
|
|
// Compressed oop support is not relevant in 32bit builds.
|
|
#ifdef _LP64
|
|
|
|
void HeapReserver::Instance::release(const ReservedSpace& reserved) {
|
|
if (reserved.is_reserved()) {
|
|
if (_fd == -1) {
|
|
if (reserved.special()) {
|
|
os::release_memory_special(reserved.base(), reserved.size());
|
|
} else{
|
|
os::release_memory(reserved.base(), reserved.size());
|
|
}
|
|
} else {
|
|
os::unmap_memory(reserved.base(), reserved.size());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tries to allocate memory of size 'size' at address requested_address with alignment 'alignment'.
|
|
// Does not check whether the reserved memory actually is at requested_address, as the memory returned
|
|
// might still fulfill the wishes of the caller.
|
|
// Assures the memory is aligned to 'alignment'.
|
|
ReservedSpace HeapReserver::Instance::try_reserve_memory(size_t size,
|
|
size_t alignment,
|
|
size_t page_size,
|
|
char* requested_address) {
|
|
// Try to reserve the memory for the heap.
|
|
log_trace(gc, heap, coops)("Trying to allocate at address " PTR_FORMAT
|
|
" heap of size " SIZE_FORMAT_X,
|
|
p2i(requested_address),
|
|
size);
|
|
|
|
ReservedSpace reserved = reserve_memory(size, alignment, page_size, requested_address);
|
|
|
|
if (reserved.is_reserved()) {
|
|
// Check alignment constraints.
|
|
assert(reserved.alignment() == alignment, "Unexpected");
|
|
assert(is_aligned(reserved.base(), alignment), "Unexpected");
|
|
return reserved;
|
|
}
|
|
|
|
// Failed
|
|
return {};
|
|
}
|
|
|
|
ReservedSpace HeapReserver::Instance::try_reserve_range(char *highest_start,
|
|
char *lowest_start,
|
|
size_t attach_point_alignment,
|
|
char *aligned_heap_base_min_address,
|
|
char *upper_bound,
|
|
size_t size,
|
|
size_t alignment,
|
|
size_t page_size) {
|
|
const size_t attach_range = highest_start - lowest_start;
|
|
// Cap num_attempts at possible number.
|
|
// At least one is possible even for 0 sized attach range.
|
|
const uint64_t num_attempts_possible = (attach_range / attach_point_alignment) + 1;
|
|
const uint64_t num_attempts_to_try = MIN2((uint64_t)HeapSearchSteps, num_attempts_possible);
|
|
|
|
const size_t stepsize = (attach_range == 0) ? // Only one try.
|
|
(size_t) highest_start : align_up(attach_range / num_attempts_to_try, attach_point_alignment);
|
|
|
|
// Try attach points from top to bottom.
|
|
for (char* attach_point = highest_start;
|
|
attach_point >= lowest_start && attach_point <= highest_start; // Avoid wrap around.
|
|
attach_point -= stepsize) {
|
|
ReservedSpace reserved = try_reserve_memory(size, alignment, page_size, attach_point);
|
|
|
|
if (reserved.is_reserved()) {
|
|
if (reserved.base() >= aligned_heap_base_min_address &&
|
|
size <= (uintptr_t)(upper_bound - reserved.base())) {
|
|
// Got a successful reservation.
|
|
return reserved;
|
|
}
|
|
|
|
release(reserved);
|
|
}
|
|
}
|
|
|
|
// Failed
|
|
return {};
|
|
}
|
|
|
|
#define SIZE_64K ((uint64_t) UCONST64( 0x10000))
|
|
#define SIZE_256M ((uint64_t) UCONST64( 0x10000000))
|
|
#define SIZE_32G ((uint64_t) UCONST64( 0x800000000))
|
|
|
|
// Helper for heap allocation. Returns an array with addresses
|
|
// (OS-specific) which are suited for disjoint base mode. Array is
|
|
// null terminated.
|
|
static char** get_attach_addresses_for_disjoint_mode() {
|
|
static uint64_t addresses[] = {
|
|
2 * SIZE_32G,
|
|
3 * SIZE_32G,
|
|
4 * SIZE_32G,
|
|
8 * SIZE_32G,
|
|
10 * SIZE_32G,
|
|
1 * SIZE_64K * SIZE_32G,
|
|
2 * SIZE_64K * SIZE_32G,
|
|
3 * SIZE_64K * SIZE_32G,
|
|
4 * SIZE_64K * SIZE_32G,
|
|
16 * SIZE_64K * SIZE_32G,
|
|
32 * SIZE_64K * SIZE_32G,
|
|
34 * SIZE_64K * SIZE_32G,
|
|
0
|
|
};
|
|
|
|
// Sort out addresses smaller than HeapBaseMinAddress. This assumes
|
|
// the array is sorted.
|
|
uint i = 0;
|
|
while (addresses[i] != 0 &&
|
|
(addresses[i] < OopEncodingHeapMax || addresses[i] < HeapBaseMinAddress)) {
|
|
i++;
|
|
}
|
|
uint start = i;
|
|
|
|
// Avoid more steps than requested.
|
|
i = 0;
|
|
while (addresses[start+i] != 0) {
|
|
if (i == HeapSearchSteps) {
|
|
addresses[start+i] = 0;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return (char**) &addresses[start];
|
|
}
|
|
|
|
// Create protection page at the beginning of the space.
|
|
static ReservedSpace establish_noaccess_prefix(const ReservedSpace& reserved, size_t noaccess_prefix) {
|
|
assert(reserved.alignment() >= os::vm_page_size(), "must be at least page size big");
|
|
assert(reserved.is_reserved(), "should only be called on a reserved memory area");
|
|
|
|
if (reserved.end() > (char *)OopEncodingHeapMax) {
|
|
if (true
|
|
WIN64_ONLY(&& !UseLargePages)
|
|
AIX_ONLY(&& (os::Aix::supports_64K_mmap_pages() || os::vm_page_size() == 4*K))) {
|
|
// Protect memory at the base of the allocated region.
|
|
if (!os::protect_memory(reserved.base(), noaccess_prefix, os::MEM_PROT_NONE, reserved.special())) {
|
|
fatal("cannot protect protection page");
|
|
}
|
|
log_debug(gc, heap, coops)("Protected page at the reserved heap base: "
|
|
PTR_FORMAT " / " INTX_FORMAT " bytes",
|
|
p2i(reserved.base()),
|
|
noaccess_prefix);
|
|
assert(CompressedOops::use_implicit_null_checks() == true, "not initialized?");
|
|
} else {
|
|
CompressedOops::set_use_implicit_null_checks(false);
|
|
}
|
|
}
|
|
|
|
return reserved.last_part(noaccess_prefix);
|
|
}
|
|
|
|
ReservedHeapSpace HeapReserver::Instance::reserve_compressed_oops_heap(const size_t size, size_t alignment, size_t page_size) {
|
|
const size_t noaccess_prefix_size = lcm(os::vm_page_size(), alignment);
|
|
const size_t granularity = os::vm_allocation_granularity();
|
|
|
|
assert(size + noaccess_prefix_size <= OopEncodingHeapMax, "can not allocate compressed oop heap for this size");
|
|
assert(is_aligned(size, granularity), "size not aligned to os::vm_allocation_granularity()");
|
|
|
|
assert(alignment >= os::vm_page_size(), "alignment too small");
|
|
assert(is_aligned(alignment, granularity), "alignment not aligned to os::vm_allocation_granularity()");
|
|
assert(is_power_of_2(alignment), "not a power of 2");
|
|
|
|
// The necessary attach point alignment for generated wish addresses.
|
|
// This is needed to increase the chance of attaching for mmap and shmat.
|
|
// AIX is the only platform that uses System V shm for reserving virtual memory.
|
|
// In this case, the required alignment of the allocated size (64K) and the alignment
|
|
// of possible start points of the memory region (256M) differ.
|
|
// This is not reflected by os_allocation_granularity().
|
|
// The logic here is dual to the one in pd_reserve_memory in os_aix.cpp
|
|
const size_t os_attach_point_alignment =
|
|
AIX_ONLY(os::vm_page_size() == 4*K ? 4*K : 256*M)
|
|
NOT_AIX(os::vm_allocation_granularity());
|
|
|
|
const size_t attach_point_alignment = lcm(alignment, os_attach_point_alignment);
|
|
|
|
char* aligned_heap_base_min_address = align_up((char*)HeapBaseMinAddress, alignment);
|
|
size_t noaccess_prefix = ((aligned_heap_base_min_address + size) > (char*)OopEncodingHeapMax) ?
|
|
noaccess_prefix_size : 0;
|
|
|
|
ReservedSpace reserved{};
|
|
|
|
// Attempt to alloc at user-given address.
|
|
if (!FLAG_IS_DEFAULT(HeapBaseMinAddress)) {
|
|
reserved = try_reserve_memory(size + noaccess_prefix, alignment, page_size, aligned_heap_base_min_address);
|
|
if (reserved.base() != aligned_heap_base_min_address) { // Enforce this exact address.
|
|
release(reserved);
|
|
reserved = {};
|
|
}
|
|
}
|
|
|
|
// Keep heap at HeapBaseMinAddress.
|
|
if (!reserved.is_reserved()) {
|
|
|
|
// Try to allocate the heap at addresses that allow efficient oop compression.
|
|
// Different schemes are tried, in order of decreasing optimization potential.
|
|
//
|
|
// For this, try_reserve_heap() is called with the desired heap base addresses.
|
|
// A call into the os layer to allocate at a given address can return memory
|
|
// at a different address than requested. Still, this might be memory at a useful
|
|
// address. try_reserve_heap() always returns this allocated memory, as only here
|
|
// the criteria for a good heap are checked.
|
|
|
|
// Attempt to allocate so that we can run without base and scale (32-Bit unscaled compressed oops).
|
|
// Give it several tries from top of range to bottom.
|
|
if (aligned_heap_base_min_address + size <= (char *)UnscaledOopHeapMax) {
|
|
|
|
// Calc address range within we try to attach (range of possible start addresses).
|
|
char* const highest_start = align_down((char *)UnscaledOopHeapMax - size, attach_point_alignment);
|
|
char* const lowest_start = align_up(aligned_heap_base_min_address, attach_point_alignment);
|
|
reserved = try_reserve_range(highest_start, lowest_start, attach_point_alignment,
|
|
aligned_heap_base_min_address, (char *)UnscaledOopHeapMax, size, alignment, page_size);
|
|
}
|
|
|
|
// zerobased: Attempt to allocate in the lower 32G.
|
|
char *zerobased_max = (char *)OopEncodingHeapMax;
|
|
|
|
// Give it several tries from top of range to bottom.
|
|
if (aligned_heap_base_min_address + size <= zerobased_max && // Zerobased theoretical possible.
|
|
((!reserved.is_reserved()) || // No previous try succeeded.
|
|
(reserved.end() > zerobased_max))) { // Unscaled delivered an arbitrary address.
|
|
|
|
// Release previous reservation
|
|
release(reserved);
|
|
|
|
// Calc address range within we try to attach (range of possible start addresses).
|
|
char *const highest_start = align_down(zerobased_max - size, attach_point_alignment);
|
|
// Need to be careful about size being guaranteed to be less
|
|
// than UnscaledOopHeapMax due to type constraints.
|
|
char *lowest_start = aligned_heap_base_min_address;
|
|
uint64_t unscaled_end = UnscaledOopHeapMax - size;
|
|
if (unscaled_end < UnscaledOopHeapMax) { // unscaled_end wrapped if size is large
|
|
lowest_start = MAX2(lowest_start, (char*)unscaled_end);
|
|
}
|
|
lowest_start = align_up(lowest_start, attach_point_alignment);
|
|
reserved = try_reserve_range(highest_start, lowest_start, attach_point_alignment,
|
|
aligned_heap_base_min_address, zerobased_max, size, alignment, page_size);
|
|
}
|
|
|
|
// Now we go for heaps with base != 0. We need a noaccess prefix to efficiently
|
|
// implement null checks.
|
|
noaccess_prefix = noaccess_prefix_size;
|
|
|
|
// Try to attach at addresses that are aligned to OopEncodingHeapMax. Disjointbase mode.
|
|
char** addresses = get_attach_addresses_for_disjoint_mode();
|
|
int i = 0;
|
|
while ((addresses[i] != nullptr) && // End of array not yet reached.
|
|
((!reserved.is_reserved()) || // No previous try succeeded.
|
|
(reserved.end() > zerobased_max && // Not zerobased or unscaled address.
|
|
// Not disjoint address.
|
|
!CompressedOops::is_disjoint_heap_base_address((address)reserved.base())))) {
|
|
|
|
// Release previous reservation
|
|
release(reserved);
|
|
|
|
char* const attach_point = addresses[i];
|
|
assert(attach_point >= aligned_heap_base_min_address, "Flag support broken");
|
|
reserved = try_reserve_memory(size + noaccess_prefix, alignment, page_size, attach_point);
|
|
i++;
|
|
}
|
|
|
|
// Last, desperate try without any placement.
|
|
if (!reserved.is_reserved()) {
|
|
log_trace(gc, heap, coops)("Trying to allocate at address null heap of size " SIZE_FORMAT_X, size + noaccess_prefix);
|
|
assert(alignment >= os::vm_page_size(), "Unexpected");
|
|
reserved = reserve_memory(size + noaccess_prefix, alignment, page_size);
|
|
}
|
|
}
|
|
|
|
// No more reserve attempts
|
|
|
|
if (reserved.is_reserved()) {
|
|
// Successfully found and reserved memory for the heap.
|
|
|
|
if (reserved.size() > size) {
|
|
// We reserved heap memory with a noaccess prefix.
|
|
|
|
assert(reserved.size() == size + noaccess_prefix, "Prefix should be included");
|
|
// It can happen we get a zerobased/unscaled heap with noaccess prefix,
|
|
// if we had to try at arbitrary address.
|
|
reserved = establish_noaccess_prefix(reserved, noaccess_prefix);
|
|
assert(reserved.size() == size, "Prefix should be gone");
|
|
return ReservedHeapSpace(reserved, noaccess_prefix);
|
|
}
|
|
|
|
// We reserved heap memory without a noaccess prefix.
|
|
return ReservedHeapSpace(reserved, 0 /* noaccess_prefix */);
|
|
}
|
|
|
|
// Failed
|
|
return {};
|
|
}
|
|
|
|
#endif // _LP64
|
|
|
|
ReservedHeapSpace HeapReserver::Instance::reserve_heap(size_t size, size_t alignment, size_t page_size) {
|
|
if (UseCompressedOops) {
|
|
#ifdef _LP64
|
|
return reserve_compressed_oops_heap(size, alignment, page_size);
|
|
#endif
|
|
} else {
|
|
return reserve_uncompressed_oops_heap(size, alignment, page_size);
|
|
}
|
|
}
|
|
|
|
ReservedHeapSpace HeapReserver::reserve(size_t size, size_t alignment, size_t page_size, const char* heap_allocation_directory) {
|
|
sanity_check_arguments(size, alignment, page_size);
|
|
|
|
assert(alignment != 0, "Precondition");
|
|
assert(is_aligned(size, alignment), "Precondition");
|
|
|
|
Instance instance(heap_allocation_directory);
|
|
|
|
return instance.reserve_heap(size, alignment, page_size);
|
|
}
|