ReservedSpace::ReservedSpace(size_t size) : _fd_for_heap(-1) { // 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();
initialize(size, alignment, page_size, NULL, false);
}
ReservedSpace::ReservedSpace(size_t size, size_t preferred_page_size) : _fd_for_heap(-1) { // When a page size is given we don't want to mix large // and normal pages. If the size is not a multiple of the // page size it will be aligned up to achieve this.
size_t alignment = os::vm_allocation_granularity();; if (preferred_page_size != (size_t)os::vm_page_size()) {
alignment = MAX2(preferred_page_size, alignment);
size = align_up(size, alignment);
}
initialize(size, alignment, preferred_page_size, NULL, false);
}
// Helper method staticbool failed_to_reserve_as_requested(char* base, char* requested_address) { if (base == requested_address || requested_address == NULL) { returnfalse; // 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));
} returntrue;
}
staticvoid 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);
}
}
staticchar* 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);
}
}
void ReservedSpace::reserve(size_t size,
size_t alignment,
size_t page_size, 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, os::vm_page_size(), true, executable);
} // Always return, not possible to fall back to reservation not using a file. return;
} elseif (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.
size_t used_page_size = page_size; char* base = NULL;
do {
base = reserve_memory_special(requested_address, size, alignment, used_page_size, executable); if (base != NULL) { break;
}
used_page_size = os::page_sizes().next_smaller(used_page_size);
} while (used_page_size > (size_t) os::vm_page_size());
if (base != NULL) { // Successful reservation using large pages.
initialize_members(base, size, alignment, used_page_size, true, executable); return;
} // Failed to reserve explicit large pages, do proper logging.
log_on_large_pages_failure(requested_address, size); // Now fall back to normal reservation.
page_size = os::vm_page_size();
}
// Not a 'special' reservation. char* base = reserve_memory(requested_address, size, alignment, -1, executable); if (base != NULL) { // Successful mapping.
initialize_members(base, size, alignment, page_size, false, executable);
}
}
void ReservedSpace::initialize(size_t size,
size_t alignment,
size_t page_size, char* requested_address, bool executable) { const size_t granularity = os::vm_allocation_granularity();
assert((size & (granularity - 1)) == 0, "size not aligned to os::vm_allocation_granularity()");
assert((alignment & (granularity - 1)) == 0, "alignment not aligned to os::vm_allocation_granularity()");
assert(alignment == 0 || is_power_of_2((intptr_t)alignment), "not a power of 2");
assert(page_size >= (size_t) os::vm_page_size(), "Invalid page size");
assert(is_power_of_2(page_size), "Invalid page size");
clear_members();
if (size == 0) { return;
}
// Adjust alignment to not be 0.
alignment = MAX2(alignment, (size_t)os::vm_page_size());
// Reserve the memory.
reserve(size, alignment, page_size, 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;
}
}
void ReservedHeapSpace::establish_noaccess_prefix() {
assert(_alignment >= (size_t)os::vm_page_size(), "must be at least page size big");
_noaccess_prefix = noaccess_prefix_size(_alignment);
if (base() && base() + _size > (char *)OopEncodingHeapMax) { if (true
WIN64_ONLY(&& !UseLargePages)
AIX_ONLY(&& os::vm_page_size() != 64*K)) { // Protect memory at the base of the allocated region. // If special, the page was committed (only matters on windows) if (!os::protect_memory(_base, _noaccess_prefix, os::MEM_PROT_NONE, _special)) {
fatal("cannot protect protection page");
}
log_debug(gc, heap, coops)("Protected page at the reserved heap base: "
PTR_FORMAT " / " INTX_FORMAT " bytes",
p2i(_base),
_noaccess_prefix);
assert(CompressedOops::use_implicit_null_checks() == true, "not initialized?");
} else {
CompressedOops::set_use_implicit_null_checks(false);
}
}
_base += _noaccess_prefix;
_size -= _noaccess_prefix;
assert(((uintptr_t)_base % _alignment == 0), "must be exactly of required alignment");
}
// 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'. // NOTE: If ReservedHeapSpace already points to some reserved memory this is freed, first. void ReservedHeapSpace::try_reserve_heap(size_t size,
size_t alignment,
size_t page_size, char* requested_address) { if (_base != NULL) { // We tried before, but we didn't like the address delivered.
release();
}
// 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);
// 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];
}
void ReservedHeapSpace::initialize_compressed_heap(const size_t size, size_t alignment, size_t page_size) {
guarantee(size + noaccess_prefix_size(alignment) <= OopEncodingHeapMax, "can not allocate compressed oop heap for this size");
guarantee(alignment == MAX2(alignment, (size_t)os::vm_page_size()), "alignment too small");
const size_t granularity = os::vm_allocation_granularity();
assert((size & (granularity - 1)) == 0, "size not aligned to os::vm_allocation_granularity()");
assert((alignment & (granularity - 1)) == 0, "alignment not aligned to os::vm_allocation_granularity()");
assert(alignment == 0 || is_power_of_2((intptr_t)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. const size_t os_attach_point_alignment =
AIX_ONLY(SIZE_256M) // Known shm boundary alignment.
NOT_AIX(os::vm_allocation_granularity()); const size_t attach_point_alignment = lcm(alignment, os_attach_point_alignment);
// Attempt to alloc at user-given address. if (!FLAG_IS_DEFAULT(HeapBaseMinAddress)) {
try_reserve_heap(size + noaccess_prefix, alignment, page_size, aligned_heap_base_min_address); if (_base != aligned_heap_base_min_address) { // Enforce this exact address.
release();
}
}
// Keep heap at HeapBaseMinAddress. if (_base == NULL) {
// 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);
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. // But leave room for the compressed class pointers, which is allocated above // the heap. char *zerobased_max = (char *)OopEncodingHeapMax; const size_t class_space = align_up(CompressedClassSpaceSize, alignment); // For small heaps, save some space for compressed class pointer // space so it can be decoded with no base. if (UseCompressedClassPointers && !UseSharedSpaces &&
OopEncodingHeapMax <= KlassEncodingMetaspaceMax &&
(uint64_t)(aligned_heap_base_min_address + size + class_space) <= KlassEncodingMetaspaceMax) {
zerobased_max = (char *)OopEncodingHeapMax - class_space;
}
// Give it several tries from top of range to bottom. if (aligned_heap_base_min_address + size <= zerobased_max && // Zerobased theoretical possible.
((_base == NULL) || // No previous try succeeded.
(_base + size > zerobased_max))) { // Unscaled delivered an arbitrary address.
// 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);
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(alignment);
// 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] && // End of array not yet reached.
((_base == NULL) || // No previous try succeeded.
(_base + size > (char *)OopEncodingHeapMax && // Not zerobased or unscaled address.
!CompressedOops::is_disjoint_heap_base_address((address)_base)))) { // Not disjoint address. char* const attach_point = addresses[i];
assert(attach_point >= aligned_heap_base_min_address, "Flag support broken");
try_reserve_heap(size + noaccess_prefix, alignment, page_size, attach_point);
i++;
}
// Last, desperate try without any placement. if (_base == NULL) {
log_trace(gc, heap, coops)("Trying to allocate at address NULL heap of size " SIZE_FORMAT_X, size + noaccess_prefix);
initialize(size + noaccess_prefix, alignment, page_size, NULL, false);
}
}
}
if (heap_allocation_directory != NULL) {
_fd_for_heap = os::create_file_for_heap(heap_allocation_directory); if (_fd_for_heap == -1) {
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(page_size) && 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.
guarantee(is_aligned(size, alignment), "set by caller");
if (UseCompressedOops) {
initialize_compressed_heap(size, alignment, page_size); if (_size > size) { // We allocated heap with noaccess prefix. // It can happen we get a zerobased/unscaled heap with noaccess prefix, // if we had to try at arbitrary address.
establish_noaccess_prefix();
}
} else {
initialize(size, alignment, page_size, NULL, false);
}
assert(markWord::encode_pointer_as_mark(_base).decode_pointer() == _base, "area must be distinguishable from marks for mark-sweep");
assert(markWord::encode_pointer_as_mark(&_base[size]).decode_pointer() == &_base[size], "area must be distinguishable from marks for mark-sweep");
if (base() != NULL) {
MemTracker::record_virtual_memory_type((address)base(), mtJavaHeap);
}
if (_fd_for_heap != -1) {
::close(_fd_for_heap);
}
}
// Reserve space for code segment. Same as Java heap only we mark this as // executable.
ReservedCodeSpace::ReservedCodeSpace(size_t r_size,
size_t rs_align,
size_t rs_page_size) : ReservedSpace() {
initialize(r_size, rs_align, rs_page_size, /*requested address*/ NULL, /*executable*/ true);
MemTracker::record_virtual_memory_type((address)base(), mtCode);
}
// When a VirtualSpace begins life at a large size, make all future expansion // and shrinking occur aligned to a granularity of large pages. This avoids // fragmentation of physical addresses that inhibits the use of large pages // by the OS virtual memory system. Empirically, we see that with a 4MB // page size, the only spaces that get handled this way are codecache and // the heap itself, both of which provide a substantial performance // boost in many benchmarks when covered by large pages. // // No attempt is made to force large page alignment at the very top and // bottom of the space if they are not aligned so already.
_lower_alignment = os::vm_page_size();
_middle_alignment = max_commit_granularity;
_upper_alignment = os::vm_page_size();
// End of each region
_lower_high_boundary = align_up(low_boundary(), middle_alignment());
_middle_high_boundary = align_down(high_boundary(), middle_alignment());
_upper_high_boundary = high_boundary();
// High address of each region
_lower_high = low_boundary();
_middle_high = lower_high_boundary();
_upper_high = middle_high_boundary();
// commit to initial size if (committed_size > 0) { if (!expand_by(committed_size)) { returnfalse;
}
} returntrue;
}
size_t VirtualSpace::actual_committed_size() const { // Special VirtualSpaces commit all reserved space up front. if (special()) { return reserved_size();
}
if (special()) { // don't commit memory if the entire space is pinned in memory
_high += bytes; returntrue;
}
char* previous_high = high(); char* unaligned_new_high = high() + bytes;
assert(unaligned_new_high <= high_boundary(), "cannot expand by more than upper boundary");
// Calculate where the new high for each of the regions should be. If // the low_boundary() and high_boundary() are LargePageSizeInBytes aligned // then the unaligned lower and upper new highs would be the // lower_high() and upper_high() respectively. char* unaligned_lower_new_high = MIN2(unaligned_new_high, lower_high_boundary()); char* unaligned_middle_new_high = MIN2(unaligned_new_high, middle_high_boundary()); char* unaligned_upper_new_high = MIN2(unaligned_new_high, upper_high_boundary());
// Align the new highs based on the regions alignment. lower and upper // alignment will always be default page size. middle alignment will be // LargePageSizeInBytes if the actual size of the virtual space is in // fact larger than LargePageSizeInBytes. char* aligned_lower_new_high = align_up(unaligned_lower_new_high, lower_alignment()); char* aligned_middle_new_high = align_up(unaligned_middle_new_high, middle_alignment()); char* aligned_upper_new_high = align_up(unaligned_upper_new_high, upper_alignment());
// Determine which regions need to grow in this expand_by call. // If you are growing in the lower region, high() must be in that // region so calculate the size based on high(). For the middle and // upper regions, determine the starting point of growth based on the // location of high(). By getting the MAX of the region's low address // (or the previous region's high address) and high(), we can tell if it // is an intra or inter region growth.
size_t lower_needs = 0; if (aligned_lower_new_high > lower_high()) {
lower_needs = pointer_delta(aligned_lower_new_high, lower_high(), sizeof(char));
}
size_t middle_needs = 0; if (aligned_middle_new_high > middle_high()) {
middle_needs = pointer_delta(aligned_middle_new_high, middle_high(), sizeof(char));
}
size_t upper_needs = 0; if (aligned_upper_new_high > upper_high()) {
upper_needs = pointer_delta(aligned_upper_new_high, upper_high(), sizeof(char));
}
// Check contiguity.
assert(low_boundary() <= lower_high() && lower_high() <= lower_high_boundary(), "high address must be contained within the region");
assert(lower_high_boundary() <= middle_high() && middle_high() <= middle_high_boundary(), "high address must be contained within the region");
assert(middle_high_boundary() <= upper_high() && upper_high() <= upper_high_boundary(), "high address must be contained within the region");
// Commit regions if (lower_needs > 0) {
assert(lower_high() + lower_needs <= lower_high_boundary(), "must not expand beyond region"); if (!commit_expanded(lower_high(), lower_needs, _lower_alignment, pre_touch, _executable)) { returnfalse;
}
_lower_high += lower_needs;
}
if (middle_needs > 0) {
assert(middle_high() + middle_needs <= middle_high_boundary(), "must not expand beyond region"); if (!commit_expanded(middle_high(), middle_needs, _middle_alignment, pre_touch, _executable)) { returnfalse;
}
_middle_high += middle_needs;
}
if (upper_needs > 0) {
assert(upper_high() + upper_needs <= upper_high_boundary(), "must not expand beyond region"); if (!commit_expanded(upper_high(), upper_needs, _upper_alignment, pre_touch, _executable)) { returnfalse;
}
_upper_high += upper_needs;
}
_high += bytes; returntrue;
}
// A page is uncommitted if the contents of the entire page is deemed unusable. // Continue to decrement the high() pointer until it reaches a page boundary // in which case that particular page can now be uncommitted. void VirtualSpace::shrink_by(size_t size) { if (committed_size() < size)
fatal("Cannot shrink virtual space to negative size");
if (special()) { // don't uncommit if the entire space is pinned in memory
_high -= size; return;
}
// Determine which regions need to shrink
size_t upper_needs = 0; if (aligned_upper_new_high < upper_high()) {
upper_needs =
pointer_delta(upper_high(), aligned_upper_new_high, sizeof(char));
}
size_t middle_needs = 0; if (aligned_middle_new_high < middle_high()) {
middle_needs =
pointer_delta(middle_high(), aligned_middle_new_high, sizeof(char));
}
size_t lower_needs = 0; if (aligned_lower_new_high < lower_high()) {
lower_needs =
pointer_delta(lower_high(), aligned_lower_new_high, sizeof(char));
}
// Check contiguity.
assert(middle_high_boundary() <= upper_high() &&
upper_high() <= upper_high_boundary(), "high address must be contained within the region");
assert(lower_high_boundary() <= middle_high() &&
middle_high() <= middle_high_boundary(), "high address must be contained within the region");
assert(low_boundary() <= lower_high() &&
lower_high() <= lower_high_boundary(), "high address must be contained within the region");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.