8265268: Unify ReservedSpace reservation code in initialize and try_reserve_heap

Reviewed-by: tschatzl, iwalulya
This commit is contained in:
Stefan Johansson 2021-04-23 12:06:05 +00:00
parent 191f1fc46c
commit 5db64c3353
2 changed files with 131 additions and 157 deletions

View File

@ -115,30 +115,82 @@ static void unmap_or_release_memory(char* base, size_t size, bool is_file_mapped
}
}
// Helper method.
static bool failed_to_reserve_as_requested(char* base, char* requested_address,
const size_t size, bool special, bool is_file_mapped = false)
{
if (base == requested_address || requested_address == NULL)
// Helper method
static bool failed_to_reserve_as_requested(char* base, char* requested_address) {
if (base == requested_address || requested_address == NULL) {
return false; // did not fail
}
if (base != NULL) {
// Different reserve address may be acceptable in other cases
// but for compressed oops heap should be at requested address.
assert(UseCompressedOops, "currently requested address used only for compressed oops");
log_debug(gc, heap, coops)("Reserved memory not at requested address: " PTR_FORMAT " vs " PTR_FORMAT, p2i(base), p2i(requested_address));
// OS ignored requested address. Try different address.
if (special) {
if (!os::release_memory_special(base, size)) {
fatal("os::release_memory_special failed");
}
} else {
unmap_or_release_memory(base, size, is_file_mapped);
}
}
return true;
}
static bool use_explicit_large_pages(bool large) {
return !os::can_commit_large_page_memory() && large;
}
static bool large_pages_requested() {
return UseLargePages &&
(!FLAG_IS_DEFAULT(UseLargePages) || !FLAG_IS_DEFAULT(LargePageSizeInBytes));
}
static char* reserve_memory(char* requested_address, const size_t size,
const size_t alignment, int fd, bool exec) {
char* base;
// 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 != 0) {
assert(is_aligned(requested_address, alignment),
"Requested address " PTR_FORMAT " must be aligned to " SIZE_FORMAT,
p2i(requested_address), alignment);
base = attempt_map_or_reserve_memory_at(requested_address, size, fd, exec);
} else {
// 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.
base = map_or_reserve_memory(size, fd, exec);
// Check alignment constraints. This is only needed when there is
// no requested address.
if (!is_aligned(base, alignment)) {
// Base not aligned, retry.
unmap_or_release_memory(base, size, fd != -1 /*is_file_mapped*/);
// Map using the requested alignment.
base = map_or_reserve_memory_aligned(size, alignment, fd, exec);
}
}
return base;
}
static char* reserve_memory_special(char* requested_address, const size_t size,
const size_t alignment, 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, requested_address, exec);
if (base != NULL) {
// Check alignment constraints.
assert(is_aligned(base, alignment),
"reserve_memory_special() returned an unaligned address, base: " PTR_FORMAT
" alignment: " SIZE_FORMAT_HEX,
p2i(base), alignment);
} else {
if (large_pages_requested()) {
log_debug(gc, heap, coops)("Reserve regular memory without large pages");
}
}
return base;
}
void ReservedSpace::clear_members() {
initialize_members(NULL, 0, 0, false, false);
}
@ -153,6 +205,50 @@ void ReservedSpace::initialize_members(char* base, size_t size, size_t alignment
_noaccess_prefix = 0;
}
void ReservedSpace::reserve(size_t size, size_t alignment, bool large,
char* requested_address,
bool executable) {
assert(is_aligned(size, alignment), "Size must be aligned to the requested alignment");
// There are basically three different cases that we need to handle below:
// - Mapping backed by a file
// - Mapping backed by explicit large pages
// - 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'.
if (_fd_for_heap != -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.
char* base = reserve_memory(requested_address, size, alignment, _fd_for_heap, executable);
if (base != NULL) {
initialize_members(base, size, alignment, true, executable);
}
// Always return, not possible to fall back to reservation not using a file.
return;
} else if (use_explicit_large_pages(large)) {
// 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.
char* base = reserve_memory_special(requested_address, size, alignment, executable);
if (base != NULL) {
// Successful reservation using large pages.
initialize_members(base, size, alignment, true, executable);
return;
}
// Failed to reserve explicit large pages, fall back to normal reservation.
}
// Not a 'special' reservation.
char* base = reserve_memory(requested_address, size, alignment, -1, executable);
if (base != NULL) {
// Successful mapping.
initialize_members(base, size, alignment, false, executable);
}
}
void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
char* requested_address,
@ -171,96 +267,18 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
return;
}
// Adjust alignment to not be 0.
alignment = MAX2(alignment, (size_t)os::vm_page_size());
// If OS doesn't support demand paging for large page memory, we need
// to use reserve_memory_special() to reserve and pin the entire region.
// If 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 we ignore the UseLargePages flag in this case.
bool special = large && !os::can_commit_large_page_memory();
if (special && _fd_for_heap != -1) {
special = false;
if (UseLargePages && (!FLAG_IS_DEFAULT(UseLargePages) ||
!FLAG_IS_DEFAULT(LargePageSizeInBytes))) {
log_debug(gc, heap)("Ignoring UseLargePages since large page support is up to the file system of the backing file for Java heap");
}
// Reserve the memory.
reserve(size, alignment, large, requested_address, executable);
// Check that the requested address is used if given.
if (failed_to_reserve_as_requested(_base, requested_address)) {
// OS ignored the requested address, release the reservation.
release();
return;
}
char* base = NULL;
if (special) {
base = os::reserve_memory_special(size, alignment, requested_address, executable);
if (base != NULL) {
if (failed_to_reserve_as_requested(base, requested_address, size, true)) {
// OS ignored requested address. Try different address.
return;
}
// Check alignment constraints.
assert((uintptr_t) base % alignment == 0,
"Large pages returned a non-aligned address, base: "
PTR_FORMAT " alignment: " SIZE_FORMAT_HEX,
p2i(base), alignment);
} else {
// failed; try to reserve regular memory below. Reservation
// should not be marked as special.
special = false;
if (UseLargePages && (!FLAG_IS_DEFAULT(UseLargePages) ||
!FLAG_IS_DEFAULT(LargePageSizeInBytes))) {
log_debug(gc, heap, coops)("Reserve regular memory without large pages");
}
}
}
if (base == NULL) {
// 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.
// If the memory was requested at a particular address, use
// os::attempt_reserve_memory_at() to avoid over mapping something
// important. If available space is not detected, return NULL.
if (requested_address != 0) {
base = attempt_map_or_reserve_memory_at(requested_address, size, _fd_for_heap, executable);
if (failed_to_reserve_as_requested(base, requested_address, size, false, _fd_for_heap != -1)) {
// OS ignored requested address. Try different address.
base = NULL;
}
} else {
base = map_or_reserve_memory(size, _fd_for_heap, executable);
}
if (base == NULL) return;
// Check alignment constraints
if ((((size_t)base) & (alignment - 1)) != 0) {
// Base not aligned, retry
unmap_or_release_memory(base, size, _fd_for_heap != -1 /*is_file_mapped*/);
// Make sure that size is aligned
size = align_up(size, alignment);
base = map_or_reserve_memory_aligned(size, alignment, _fd_for_heap, executable);
if (requested_address != 0 &&
failed_to_reserve_as_requested(base, requested_address, size, false, _fd_for_heap != -1)) {
// As a result of the alignment constraints, the allocated base differs
// from the requested address. Return back to the caller who can
// take remedial action (like try again without a requested address).
assert(_base == NULL, "should be");
return;
}
}
}
// If heap is reserved with a backing file, the entire space has been committed. So set the special flag to true
if (_fd_for_heap != -1) {
special = true;
}
// Done
initialize_members(base, size, alignment, special, executable);
}
ReservedSpace ReservedSpace::first_part(size_t partition_size, size_t alignment) {
@ -375,69 +393,16 @@ void ReservedHeapSpace::try_reserve_heap(size_t size,
release();
}
// If OS doesn't support demand paging for large page memory, we need
// to use reserve_memory_special() to reserve and pin the entire region.
// If 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 we ignore the UseLargePages flag in this case.
bool special = large && !os::can_commit_large_page_memory();
if (special && _fd_for_heap != -1) {
special = false;
if (UseLargePages && (!FLAG_IS_DEFAULT(UseLargePages) ||
!FLAG_IS_DEFAULT(LargePageSizeInBytes))) {
log_debug(gc, heap)("Cannot allocate large pages for Java Heap when AllocateHeapAt option is set.");
}
}
char* base = NULL;
// 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_HEX,
p2i(requested_address),
size);
if (special) {
base = os::reserve_memory_special(size, alignment, requested_address, false);
reserve(size, alignment, large, requested_address, false);
if (base != NULL) {
// Check alignment constraints.
assert((uintptr_t) base % alignment == 0,
"Large pages returned a non-aligned address, base: "
PTR_FORMAT " alignment: " SIZE_FORMAT_HEX,
p2i(base), alignment);
}
}
if (base == NULL) {
// Failed; try to reserve regular memory below. Reservation
// should not be marked as special.
special = false;
if (UseLargePages && (!FLAG_IS_DEFAULT(UseLargePages) ||
!FLAG_IS_DEFAULT(LargePageSizeInBytes))) {
log_debug(gc, heap, coops)("Reserve regular memory without large pages");
}
if (requested_address != 0) {
base = attempt_map_or_reserve_memory_at(requested_address, size, _fd_for_heap, executable());
} else {
// Optimistically assume that the OSes returns an aligned base pointer.
// When reserving a large address range, most OSes seem to align to at
// least 64K.
// If the returned memory is not aligned we will release and retry.
base = map_or_reserve_memory(size, _fd_for_heap, executable());
}
}
if (base == NULL) { return; }
// If heap is reserved with a backing file, the entire space has been committed. So set the special flag to true
if (_fd_for_heap != -1) {
special = true;
}
// Done
initialize_members(base, size, alignment, special, false);
// Check alignment constraints
if (!is_aligned(base, alignment)) {
// Check alignment constraints.
if (is_reserved() && !is_aligned(_base, _alignment)) {
// Base not aligned, retry.
release();
}
@ -641,6 +606,12 @@ ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large,
vm_exit_during_initialization(
err_msg("Could not create file for Heap at location %s", heap_allocation_directory));
}
// 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.
// If requested, let the user know that explicit large pages can't be used.
if (use_explicit_large_pages(large) && large_pages_requested()) {
log_debug(gc, heap)("Cannot allocate explicit large pages for Java Heap when AllocateHeapAt option is set.");
}
}
// Heap size should be aligned to alignment, too.

View File

@ -63,6 +63,9 @@ class ReservedSpace {
char* requested_address,
bool executable);
void reserve(size_t size, size_t alignment, bool large,
char* requested_address,
bool executable);
public:
// Constructor
ReservedSpace();