8190308: Implementation: JEP 316: Heap Allocation on Alternative Memory Devices
Sub-task to be used for implementation of JEP 316: Support heap allocation on alternative memory devices Reviewed-by: sangheki, tschatzl
This commit is contained in:
parent
3faa620f4c
commit
3fc999a1fe
src/hotspot
os
aix
bsd
linux
posix
solaris
windows
share
test/hotspot/jtreg/gc
@ -2490,6 +2490,22 @@ bool os::can_execute_large_page_memory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) {
|
||||
assert(file_desc >= 0, "file_desc is not valid");
|
||||
char* result = NULL;
|
||||
|
||||
// Always round to os::vm_page_size(), which may be larger than 4K.
|
||||
bytes = align_up(bytes, os::vm_page_size());
|
||||
result = reserve_mmaped_memory(bytes, requested_addr, 0);
|
||||
|
||||
if (result != NULL) {
|
||||
if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) {
|
||||
vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reserve memory at an arbitrary address, only if that area is
|
||||
// available (and not reserved for something else).
|
||||
char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr) {
|
||||
|
@ -2350,6 +2350,17 @@ bool os::can_execute_large_page_memory() {
|
||||
return UseHugeTLBFS;
|
||||
}
|
||||
|
||||
char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) {
|
||||
assert(file_desc >= 0, "file_desc is not valid");
|
||||
char* result = pd_attempt_reserve_memory_at(bytes, requested_addr);
|
||||
if (result != NULL) {
|
||||
if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) {
|
||||
vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reserve memory at an arbitrary address, only if that area is
|
||||
// available (and not reserved for something else).
|
||||
|
||||
|
@ -130,6 +130,7 @@
|
||||
#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF)
|
||||
|
||||
#define LARGEPAGES_BIT (1 << 6)
|
||||
#define DAX_SHARED_BIT (1 << 8)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// global variables
|
||||
julong os::Linux::_physical_memory = 0;
|
||||
@ -3370,10 +3371,13 @@ bool os::Linux::hugetlbfs_sanity_check(bool warn, size_t page_size) {
|
||||
// effective only if the bit 2 is cleared)
|
||||
// - (bit 5) hugetlb private memory
|
||||
// - (bit 6) hugetlb shared memory
|
||||
// - (bit 7) dax private memory
|
||||
// - (bit 8) dax shared memory
|
||||
//
|
||||
static void set_coredump_filter(void) {
|
||||
static void set_coredump_filter(bool largepages, bool dax_shared) {
|
||||
FILE *f;
|
||||
long cdm;
|
||||
bool filter_changed = false;
|
||||
|
||||
if ((f = fopen("/proc/self/coredump_filter", "r+")) == NULL) {
|
||||
return;
|
||||
@ -3386,8 +3390,15 @@ static void set_coredump_filter(void) {
|
||||
|
||||
rewind(f);
|
||||
|
||||
if ((cdm & LARGEPAGES_BIT) == 0) {
|
||||
if (largepages && (cdm & LARGEPAGES_BIT) == 0) {
|
||||
cdm |= LARGEPAGES_BIT;
|
||||
filter_changed = true;
|
||||
}
|
||||
if (dax_shared && (cdm & DAX_SHARED_BIT) == 0) {
|
||||
cdm |= DAX_SHARED_BIT;
|
||||
filter_changed = true;
|
||||
}
|
||||
if (filter_changed) {
|
||||
fprintf(f, "%#lx", cdm);
|
||||
}
|
||||
|
||||
@ -3526,7 +3537,7 @@ void os::large_page_init() {
|
||||
size_t large_page_size = Linux::setup_large_page_size();
|
||||
UseLargePages = Linux::setup_large_page_type(large_page_size);
|
||||
|
||||
set_coredump_filter();
|
||||
set_coredump_filter(true /*largepages*/, false /*dax_shared*/);
|
||||
}
|
||||
|
||||
#ifndef SHM_HUGETLB
|
||||
@ -3897,6 +3908,17 @@ bool os::can_execute_large_page_memory() {
|
||||
return UseTransparentHugePages || UseHugeTLBFS;
|
||||
}
|
||||
|
||||
char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) {
|
||||
assert(file_desc >= 0, "file_desc is not valid");
|
||||
char* result = pd_attempt_reserve_memory_at(bytes, requested_addr);
|
||||
if (result != NULL) {
|
||||
if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) {
|
||||
vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reserve memory at an arbitrary address, only if that area is
|
||||
// available (and not reserved for something else).
|
||||
|
||||
@ -5008,6 +5030,9 @@ jint os::init_2(void) {
|
||||
// initialize thread priority policy
|
||||
prio_init();
|
||||
|
||||
if (!FLAG_IS_DEFAULT(AllocateHeapAt)) {
|
||||
set_coredump_filter(false /*largepages*/, true /*dax_shared*/);
|
||||
}
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <time.h>
|
||||
@ -52,6 +53,20 @@
|
||||
#endif
|
||||
#define IS_VALID_PID(p) (p > 0 && p < MAX_PID)
|
||||
|
||||
#ifndef MAP_ANONYMOUS
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#endif
|
||||
|
||||
#define check_with_errno(check_type, cond, msg) \
|
||||
do { \
|
||||
int err = errno; \
|
||||
check_type(cond, "%s; error='%s' (errno=%s)", msg, os::strerror(err), \
|
||||
os::errno_name(err)); \
|
||||
} while (false)
|
||||
|
||||
#define assert_with_errno(cond, msg) check_with_errno(assert, cond, msg)
|
||||
#define guarantee_with_errno(cond, msg) check_with_errno(guarantee, cond, msg)
|
||||
|
||||
// Check core dump limit and report possible place where core can be found
|
||||
void os::check_dump_limit(char* buffer, size_t bufferSize) {
|
||||
if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) {
|
||||
@ -145,10 +160,124 @@ void os::wait_for_keypress_at_exit(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
int os::create_file_for_heap(const char* dir) {
|
||||
|
||||
const char name_template[] = "/jvmheap.XXXXXX";
|
||||
|
||||
char *fullname = (char*)os::malloc((strlen(dir) + strlen(name_template) + 1), mtInternal);
|
||||
if (fullname == NULL) {
|
||||
vm_exit_during_initialization(err_msg("Malloc failed during creation of backing file for heap (%s)", os::strerror(errno)));
|
||||
return -1;
|
||||
}
|
||||
(void)strncpy(fullname, dir, strlen(dir)+1);
|
||||
(void)strncat(fullname, name_template, strlen(name_template));
|
||||
|
||||
os::native_path(fullname);
|
||||
|
||||
sigset_t set, oldset;
|
||||
int ret = sigfillset(&set);
|
||||
assert_with_errno(ret == 0, "sigfillset returned error");
|
||||
|
||||
// set the file creation mask.
|
||||
mode_t file_mode = S_IRUSR | S_IWUSR;
|
||||
|
||||
// create a new file.
|
||||
int fd = mkstemp(fullname);
|
||||
|
||||
if (fd < 0) {
|
||||
warning("Could not create file for heap with template %s", fullname);
|
||||
os::free(fullname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// delete the name from the filesystem. When 'fd' is closed, the file (and space) will be deleted.
|
||||
ret = unlink(fullname);
|
||||
assert_with_errno(ret == 0, "unlink returned error");
|
||||
|
||||
os::free(fullname);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static char* reserve_mmapped_memory(size_t bytes, char* requested_addr) {
|
||||
char * addr;
|
||||
int flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS;
|
||||
if (requested_addr != NULL) {
|
||||
assert((uintptr_t)requested_addr % os::vm_page_size() == 0, "Requested address should be aligned to OS page size");
|
||||
flags |= MAP_FIXED;
|
||||
}
|
||||
|
||||
// Map reserved/uncommitted pages PROT_NONE so we fail early if we
|
||||
// touch an uncommitted page. Otherwise, the read/write might
|
||||
// succeed if we have enough swap space to back the physical page.
|
||||
addr = (char*)::mmap(requested_addr, bytes, PROT_NONE,
|
||||
flags, -1, 0);
|
||||
|
||||
if (addr != MAP_FAILED) {
|
||||
MemTracker::record_virtual_memory_reserve((address)addr, bytes, CALLER_PC);
|
||||
return addr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int util_posix_fallocate(int fd, off_t offset, off_t len) {
|
||||
#ifdef __APPLE__
|
||||
fstore_t store = { F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, len };
|
||||
// First we try to get a continuous chunk of disk space
|
||||
int ret = fcntl(fd, F_PREALLOCATE, &store);
|
||||
if (ret == -1) {
|
||||
// Maybe we are too fragmented, try to allocate non-continuous range
|
||||
store.fst_flags = F_ALLOCATEALL;
|
||||
ret = fcntl(fd, F_PREALLOCATE, &store);
|
||||
}
|
||||
if(ret != -1) {
|
||||
return ftruncate(fd, len);
|
||||
}
|
||||
return -1;
|
||||
#else
|
||||
return posix_fallocate(fd, offset, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Map the given address range to the provided file descriptor.
|
||||
char* os::map_memory_to_file(char* base, size_t size, int fd) {
|
||||
assert(fd != -1, "File descriptor is not valid");
|
||||
|
||||
// allocate space for the file
|
||||
if (util_posix_fallocate(fd, 0, (off_t)size) != 0) {
|
||||
vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int prot = PROT_READ | PROT_WRITE;
|
||||
int flags = MAP_SHARED;
|
||||
if (base != NULL) {
|
||||
flags |= MAP_FIXED;
|
||||
}
|
||||
char* addr = (char*)mmap(base, size, prot, flags, fd, 0);
|
||||
|
||||
if (addr == MAP_FAILED) {
|
||||
return NULL;
|
||||
}
|
||||
if (base != NULL && addr != base) {
|
||||
if (!os::release_memory(addr, size)) {
|
||||
warning("Could not release memory on unsuccessful file mapping");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd) {
|
||||
assert(fd != -1, "File descriptor is not valid");
|
||||
assert(base != NULL, "Base cannot be NULL");
|
||||
|
||||
return map_memory_to_file(base, size, fd);
|
||||
}
|
||||
|
||||
// Multiple threads can race in this code, and can remap over each other with MAP_FIXED,
|
||||
// so on posix, unmap the section at the start and at the end of the chunk that we mapped
|
||||
// rather than unmapping and remapping the whole chunk to get requested alignment.
|
||||
char* os::reserve_memory_aligned(size_t size, size_t alignment) {
|
||||
char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) {
|
||||
assert((alignment & (os::vm_allocation_granularity() - 1)) == 0,
|
||||
"Alignment must be a multiple of allocation granularity (page size)");
|
||||
assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned");
|
||||
@ -156,7 +285,20 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment) {
|
||||
size_t extra_size = size + alignment;
|
||||
assert(extra_size >= size, "overflow, size is too large to allow alignment");
|
||||
|
||||
char* extra_base = os::reserve_memory(extra_size, NULL, alignment);
|
||||
char* extra_base;
|
||||
if (file_desc != -1) {
|
||||
// For file mapping, we do not call os:reserve_memory(extra_size, NULL, alignment, file_desc) because
|
||||
// we need to deal with shrinking of the file space later when we release extra memory after alignment.
|
||||
// We also cannot called os:reserve_memory() with file_desc set to -1 because on aix we might get SHM memory.
|
||||
// So here to call a helper function while reserve memory for us. After we have a aligned base,
|
||||
// we will replace anonymous mapping with file mapping.
|
||||
extra_base = reserve_mmapped_memory(extra_size, NULL);
|
||||
if (extra_base != NULL) {
|
||||
MemTracker::record_virtual_memory_reserve((address)extra_base, extra_size, CALLER_PC);
|
||||
}
|
||||
} else {
|
||||
extra_base = os::reserve_memory(extra_size, NULL, alignment);
|
||||
}
|
||||
|
||||
if (extra_base == NULL) {
|
||||
return NULL;
|
||||
@ -183,6 +325,13 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment) {
|
||||
os::release_memory(extra_base + begin_offset + size, end_offset);
|
||||
}
|
||||
|
||||
if (file_desc != -1) {
|
||||
// After we have an aligned address, we can replace anonymous mapping with file mapping
|
||||
if (replace_existing_mapping_with_file_mapping(aligned_base, size, file_desc) == NULL) {
|
||||
vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
|
||||
}
|
||||
MemTracker::record_virtual_memory_commit((address)aligned_base, size, CALLER_PC);
|
||||
}
|
||||
return aligned_base;
|
||||
}
|
||||
|
||||
@ -1348,16 +1497,6 @@ void os::ThreadCrashProtection::check_crash_protection(int sig,
|
||||
}
|
||||
}
|
||||
|
||||
#define check_with_errno(check_type, cond, msg) \
|
||||
do { \
|
||||
int err = errno; \
|
||||
check_type(cond, "%s; error='%s' (errno=%s)", msg, os::strerror(err), \
|
||||
os::errno_name(err)); \
|
||||
} while (false)
|
||||
|
||||
#define assert_with_errno(cond, msg) check_with_errno(assert, cond, msg)
|
||||
#define guarantee_with_errno(cond, msg) check_with_errno(guarantee, cond, msg)
|
||||
|
||||
// POSIX unamed semaphores are not supported on OS X.
|
||||
#ifndef __APPLE__
|
||||
|
||||
|
@ -2585,6 +2585,17 @@ char* os::pd_reserve_memory(size_t bytes, char* requested_addr,
|
||||
return addr;
|
||||
}
|
||||
|
||||
char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) {
|
||||
assert(file_desc >= 0, "file_desc is not valid");
|
||||
char* result = pd_attempt_reserve_memory_at(bytes, requested_addr);
|
||||
if (result != NULL) {
|
||||
if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) {
|
||||
vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reserve memory at an arbitrary address, only if that area is
|
||||
// available (and not reserved for something else).
|
||||
|
||||
|
@ -2904,6 +2904,75 @@ void os::large_page_init() {
|
||||
UseLargePages = success;
|
||||
}
|
||||
|
||||
int os::create_file_for_heap(const char* dir) {
|
||||
|
||||
const char name_template[] = "/jvmheap.XXXXXX";
|
||||
char *fullname = (char*)os::malloc((strlen(dir) + strlen(name_template) + 1), mtInternal);
|
||||
if (fullname == NULL) {
|
||||
vm_exit_during_initialization(err_msg("Malloc failed during creation of backing file for heap (%s)", os::strerror(errno)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)strncpy(fullname, dir, strlen(dir)+1);
|
||||
(void)strncat(fullname, name_template, strlen(name_template));
|
||||
|
||||
os::native_path(fullname);
|
||||
|
||||
char *path = _mktemp(fullname);
|
||||
if (path == NULL) {
|
||||
warning("_mktemp could not create file name from template %s (%s)", fullname, os::strerror(errno));
|
||||
os::free(fullname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd = _open(path, O_RDWR | O_CREAT | O_TEMPORARY | O_EXCL, S_IWRITE | S_IREAD);
|
||||
|
||||
os::free(fullname);
|
||||
if (fd < 0) {
|
||||
warning("Problem opening file for heap (%s)", os::strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
// If 'base' is not NULL, function will return NULL if it cannot get 'base'
|
||||
char* os::map_memory_to_file(char* base, size_t size, int fd) {
|
||||
assert(fd != -1, "File descriptor is not valid");
|
||||
|
||||
HANDLE fh = (HANDLE)_get_osfhandle(fd);
|
||||
#ifdef _LP64
|
||||
HANDLE fileMapping = CreateFileMapping(fh, NULL, PAGE_READWRITE,
|
||||
(DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), NULL);
|
||||
#else
|
||||
HANDLE fileMapping = CreateFileMapping(fh, NULL, PAGE_READWRITE,
|
||||
0, (DWORD)size, NULL);
|
||||
#endif
|
||||
if (fileMapping == NULL) {
|
||||
if (GetLastError() == ERROR_DISK_FULL) {
|
||||
vm_exit_during_initialization(err_msg("Could not allocate sufficient disk space for Java heap"));
|
||||
}
|
||||
else {
|
||||
vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LPVOID addr = MapViewOfFileEx(fileMapping, FILE_MAP_WRITE, 0, 0, size, base);
|
||||
|
||||
CloseHandle(fileMapping);
|
||||
|
||||
return (char*)addr;
|
||||
}
|
||||
|
||||
char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd) {
|
||||
assert(fd != -1, "File descriptor is not valid");
|
||||
assert(base != NULL, "Base address cannot be NULL");
|
||||
|
||||
release_memory(base, size);
|
||||
return map_memory_to_file(base, size, fd);
|
||||
}
|
||||
|
||||
// On win32, one cannot release just a part of reserved memory, it's an
|
||||
// all or nothing deal. When we split a reservation, we must break the
|
||||
// reservation into two reservations.
|
||||
@ -2923,7 +2992,7 @@ void os::pd_split_reserved_memory(char *base, size_t size, size_t split,
|
||||
// Multiple threads can race in this code but it's not possible to unmap small sections of
|
||||
// virtual space to get requested alignment, like posix-like os's.
|
||||
// Windows prevents multiple thread from remapping over each other so this loop is thread-safe.
|
||||
char* os::reserve_memory_aligned(size_t size, size_t alignment) {
|
||||
char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) {
|
||||
assert((alignment & (os::vm_allocation_granularity() - 1)) == 0,
|
||||
"Alignment must be a multiple of allocation granularity (page size)");
|
||||
assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned");
|
||||
@ -2934,16 +3003,20 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment) {
|
||||
char* aligned_base = NULL;
|
||||
|
||||
do {
|
||||
char* extra_base = os::reserve_memory(extra_size, NULL, alignment);
|
||||
char* extra_base = os::reserve_memory(extra_size, NULL, alignment, file_desc);
|
||||
if (extra_base == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Do manual alignment
|
||||
aligned_base = align_up(extra_base, alignment);
|
||||
|
||||
os::release_memory(extra_base, extra_size);
|
||||
if (file_desc != -1) {
|
||||
os::unmap_memory(extra_base, extra_size);
|
||||
} else {
|
||||
os::release_memory(extra_base, extra_size);
|
||||
}
|
||||
|
||||
aligned_base = os::reserve_memory(size, aligned_base);
|
||||
aligned_base = os::reserve_memory(size, aligned_base, 0, file_desc);
|
||||
|
||||
} while (aligned_base == NULL);
|
||||
|
||||
@ -2989,6 +3062,11 @@ char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr) {
|
||||
return reserve_memory(bytes, requested_addr);
|
||||
}
|
||||
|
||||
char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) {
|
||||
assert(file_desc >= 0, "file_desc is not valid");
|
||||
return map_memory_to_file(requested_addr, bytes, file_desc);
|
||||
}
|
||||
|
||||
size_t os::large_page_size() {
|
||||
return _large_page_size;
|
||||
}
|
||||
|
@ -856,7 +856,7 @@ ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) {
|
||||
|| use_large_pages, "Wrong alignment to use large pages");
|
||||
|
||||
// Now create the space.
|
||||
ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages);
|
||||
ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages, AllocateHeapAt);
|
||||
|
||||
if (total_rs.is_reserved()) {
|
||||
assert((total_reserved == total_rs.size()) && ((uintptr_t)total_rs.base() % alignment == 0),
|
||||
@ -870,6 +870,9 @@ ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) {
|
||||
Universe::set_narrow_oop_base((address)total_rs.compressed_oop_base());
|
||||
}
|
||||
|
||||
if (AllocateHeapAt != NULL) {
|
||||
log_info(gc,heap)("Successfully allocated Java heap at location %s", AllocateHeapAt);
|
||||
}
|
||||
return total_rs;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2017, 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
|
||||
@ -35,10 +35,10 @@
|
||||
|
||||
// Dummy constructor
|
||||
ReservedSpace::ReservedSpace() : _base(NULL), _size(0), _noaccess_prefix(0),
|
||||
_alignment(0), _special(false), _executable(false) {
|
||||
_alignment(0), _special(false), _executable(false), _fd_for_heap(-1) {
|
||||
}
|
||||
|
||||
ReservedSpace::ReservedSpace(size_t size, size_t preferred_page_size) {
|
||||
ReservedSpace::ReservedSpace(size_t size, size_t preferred_page_size) : _fd_for_heap(-1) {
|
||||
bool has_preferred_page_size = preferred_page_size != 0;
|
||||
// Want to use large pages where possible and pad with small pages.
|
||||
size_t page_size = has_preferred_page_size ? preferred_page_size : os::page_size_for_region_unaligned(size, 1);
|
||||
@ -59,19 +59,30 @@ ReservedSpace::ReservedSpace(size_t size, size_t preferred_page_size) {
|
||||
|
||||
ReservedSpace::ReservedSpace(size_t size, size_t alignment,
|
||||
bool large,
|
||||
char* requested_address) {
|
||||
char* requested_address) : _fd_for_heap(-1) {
|
||||
initialize(size, alignment, large, requested_address, false);
|
||||
}
|
||||
|
||||
ReservedSpace::ReservedSpace(size_t size, size_t alignment,
|
||||
bool large,
|
||||
bool executable) {
|
||||
bool executable) : _fd_for_heap(-1) {
|
||||
initialize(size, alignment, large, NULL, executable);
|
||||
}
|
||||
|
||||
// Helper method
|
||||
static void unmap_or_release_memory(char* base, size_t size, bool is_file_mapped) {
|
||||
if (is_file_mapped) {
|
||||
if (!os::unmap_memory(base, size)) {
|
||||
fatal("os::unmap_memory failed");
|
||||
}
|
||||
} else if (!os::release_memory(base, size)) {
|
||||
fatal("os::release_memory failed");
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method.
|
||||
static bool failed_to_reserve_as_requested(char* base, char* requested_address,
|
||||
const size_t size, bool special)
|
||||
const size_t size, bool special, bool is_file_mapped = false)
|
||||
{
|
||||
if (base == requested_address || requested_address == NULL)
|
||||
return false; // did not fail
|
||||
@ -87,9 +98,7 @@ static bool failed_to_reserve_as_requested(char* base, char* requested_address,
|
||||
fatal("os::release_memory_special failed");
|
||||
}
|
||||
} else {
|
||||
if (!os::release_memory(base, size)) {
|
||||
fatal("os::release_memory failed");
|
||||
}
|
||||
unmap_or_release_memory(base, size, is_file_mapped);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -120,7 +129,18 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
char* base = NULL;
|
||||
|
||||
if (special) {
|
||||
@ -157,13 +177,13 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
|
||||
// important. If available space is not detected, return NULL.
|
||||
|
||||
if (requested_address != 0) {
|
||||
base = os::attempt_reserve_memory_at(size, requested_address);
|
||||
if (failed_to_reserve_as_requested(base, requested_address, size, false)) {
|
||||
base = os::attempt_reserve_memory_at(size, requested_address, _fd_for_heap);
|
||||
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 = os::reserve_memory(size, NULL, alignment);
|
||||
base = os::reserve_memory(size, NULL, alignment, _fd_for_heap);
|
||||
}
|
||||
|
||||
if (base == NULL) return;
|
||||
@ -171,13 +191,14 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
|
||||
// Check alignment constraints
|
||||
if ((((size_t)base) & (alignment - 1)) != 0) {
|
||||
// Base not aligned, retry
|
||||
if (!os::release_memory(base, size)) fatal("os::release_memory failed");
|
||||
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 = os::reserve_memory_aligned(size, alignment);
|
||||
base = os::reserve_memory_aligned(size, alignment, _fd_for_heap);
|
||||
|
||||
if (requested_address != 0 &&
|
||||
failed_to_reserve_as_requested(base, requested_address, size, false)) {
|
||||
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).
|
||||
@ -190,6 +211,10 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
|
||||
_base = base;
|
||||
_size = size;
|
||||
_alignment = alignment;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -252,7 +277,11 @@ void ReservedSpace::release() {
|
||||
char *real_base = _base - _noaccess_prefix;
|
||||
const size_t real_size = _size + _noaccess_prefix;
|
||||
if (special()) {
|
||||
os::release_memory_special(real_base, real_size);
|
||||
if (_fd_for_heap != -1) {
|
||||
os::unmap_memory(real_base, real_size);
|
||||
} else {
|
||||
os::release_memory_special(real_base, real_size);
|
||||
}
|
||||
} else{
|
||||
os::release_memory(real_base, real_size);
|
||||
}
|
||||
@ -313,7 +342,17 @@ void ReservedHeapSpace::try_reserve_heap(size_t 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)("Cannot allocate large pages for Java Heap when AllocateHeapAt option is set.");
|
||||
}
|
||||
}
|
||||
char* base = NULL;
|
||||
|
||||
log_trace(gc, heap, coops)("Trying to allocate at address " PTR_FORMAT
|
||||
@ -350,9 +389,9 @@ void ReservedHeapSpace::try_reserve_heap(size_t size,
|
||||
// important. If available space is not detected, return NULL.
|
||||
|
||||
if (requested_address != 0) {
|
||||
base = os::attempt_reserve_memory_at(size, requested_address);
|
||||
base = os::attempt_reserve_memory_at(size, requested_address, _fd_for_heap);
|
||||
} else {
|
||||
base = os::reserve_memory(size, NULL, alignment);
|
||||
base = os::reserve_memory(size, NULL, alignment, _fd_for_heap);
|
||||
}
|
||||
}
|
||||
if (base == NULL) { return; }
|
||||
@ -362,6 +401,11 @@ void ReservedHeapSpace::try_reserve_heap(size_t size,
|
||||
_size = size;
|
||||
_alignment = alignment;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Check alignment constraints
|
||||
if ((((size_t)base) & (alignment - 1)) != 0) {
|
||||
// Base not aligned, retry.
|
||||
@ -556,12 +600,20 @@ void ReservedHeapSpace::initialize_compressed_heap(const size_t size, size_t ali
|
||||
}
|
||||
}
|
||||
|
||||
ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large) : ReservedSpace() {
|
||||
ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large, const char* heap_allocation_directory) : ReservedSpace() {
|
||||
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// Heap size should be aligned to alignment, too.
|
||||
guarantee(is_aligned(size, alignment), "set by caller");
|
||||
|
||||
@ -585,6 +637,10 @@ ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large)
|
||||
if (base() != NULL) {
|
||||
MemTracker::record_virtual_memory_type((address)base(), mtJavaHeap);
|
||||
}
|
||||
|
||||
if (_fd_for_heap != -1) {
|
||||
os::close(_fd_for_heap);
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve space for code segment. Same as Java heap only we mark this as
|
||||
|
@ -37,6 +37,7 @@ class ReservedSpace VALUE_OBJ_CLASS_SPEC {
|
||||
size_t _noaccess_prefix;
|
||||
size_t _alignment;
|
||||
bool _special;
|
||||
int _fd_for_heap;
|
||||
private:
|
||||
bool _executable;
|
||||
|
||||
@ -115,7 +116,9 @@ class ReservedHeapSpace : public ReservedSpace {
|
||||
void establish_noaccess_prefix();
|
||||
public:
|
||||
// Constructor. Tries to find a heap that is good for compressed oops.
|
||||
ReservedHeapSpace(size_t size, size_t forced_base_alignment, bool large);
|
||||
// heap_allocation_directory is the path to the backing memory for Java heap. When set, Java heap will be allocated
|
||||
// on the device which is managed by the file system where the directory resides.
|
||||
ReservedHeapSpace(size_t size, size_t forced_base_alignment, bool large, const char* heap_allocation_directory = NULL);
|
||||
// Returns the base to be used for compression, i.e. so that null can be
|
||||
// encoded safely and implicit null checks can work.
|
||||
char *compressed_oop_base() { return _base - _noaccess_prefix; }
|
||||
|
@ -2223,6 +2223,11 @@ bool Arguments::check_vm_args_consistency() {
|
||||
LoopStripMiningIterShortLoop = LoopStripMiningIter / 10;
|
||||
}
|
||||
#endif
|
||||
if (!FLAG_IS_DEFAULT(AllocateHeapAt)) {
|
||||
if ((UseNUMAInterleaving && !FLAG_IS_DEFAULT(UseNUMAInterleaving)) || (UseNUMA && !FLAG_IS_DEFAULT(UseNUMA))) {
|
||||
log_warning(arguments) ("NUMA support for Heap depends on the file system when AllocateHeapAt option is used.\n");
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -4304,7 +4309,9 @@ jint Arguments::apply_ergo() {
|
||||
|
||||
jint Arguments::adjust_after_os() {
|
||||
if (UseNUMA) {
|
||||
if (UseParallelGC || UseParallelOldGC) {
|
||||
if (!FLAG_IS_DEFAULT(AllocateHeapAt)) {
|
||||
FLAG_SET_ERGO(bool, UseNUMA, false);
|
||||
} else if (UseParallelGC || UseParallelOldGC) {
|
||||
if (FLAG_IS_DEFAULT(MinHeapDeltaBytes)) {
|
||||
FLAG_SET_DEFAULT(MinHeapDeltaBytes, 64*M);
|
||||
}
|
||||
|
@ -4083,7 +4083,11 @@ public:
|
||||
diagnostic(bool, CompilerDirectivesPrint, false, \
|
||||
"Print compiler directives on installation.") \
|
||||
diagnostic(int, CompilerDirectivesLimit, 50, \
|
||||
"Limit on number of compiler directives.")
|
||||
"Limit on number of compiler directives.") \
|
||||
\
|
||||
product(ccstr, AllocateHeapAt, NULL, \
|
||||
"Path to the directoy where a temporary file will be created " \
|
||||
"to use as the backing store for Java Heap.")
|
||||
|
||||
|
||||
/*
|
||||
|
@ -1665,10 +1665,21 @@ bool os::create_stack_guard_pages(char* addr, size_t bytes) {
|
||||
return os::pd_create_stack_guard_pages(addr, bytes);
|
||||
}
|
||||
|
||||
char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint) {
|
||||
char* result = pd_reserve_memory(bytes, addr, alignment_hint);
|
||||
if (result != NULL) {
|
||||
MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);
|
||||
char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint, int file_desc) {
|
||||
char* result = NULL;
|
||||
|
||||
if (file_desc != -1) {
|
||||
// Could have called pd_reserve_memory() followed by replace_existing_mapping_with_file_mapping(),
|
||||
// but AIX may use SHM in which case its more trouble to detach the segment and remap memory to the file.
|
||||
result = os::map_memory_to_file(addr, bytes, file_desc);
|
||||
if (result != NULL) {
|
||||
MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC);
|
||||
}
|
||||
} else {
|
||||
result = pd_reserve_memory(bytes, addr, alignment_hint);
|
||||
if (result != NULL) {
|
||||
MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -1685,10 +1696,18 @@ char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint,
|
||||
return result;
|
||||
}
|
||||
|
||||
char* os::attempt_reserve_memory_at(size_t bytes, char* addr) {
|
||||
char* result = pd_attempt_reserve_memory_at(bytes, addr);
|
||||
if (result != NULL) {
|
||||
MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);
|
||||
char* os::attempt_reserve_memory_at(size_t bytes, char* addr, int file_desc) {
|
||||
char* result = NULL;
|
||||
if (file_desc != -1) {
|
||||
result = pd_attempt_reserve_memory_at(bytes, addr, file_desc);
|
||||
if (result != NULL) {
|
||||
MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC);
|
||||
}
|
||||
} else {
|
||||
result = pd_attempt_reserve_memory_at(bytes, addr);
|
||||
if (result != NULL) {
|
||||
MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -108,8 +108,9 @@ class os: AllStatic {
|
||||
}
|
||||
|
||||
static char* pd_reserve_memory(size_t bytes, char* addr = 0,
|
||||
size_t alignment_hint = 0);
|
||||
size_t alignment_hint = 0);
|
||||
static char* pd_attempt_reserve_memory_at(size_t bytes, char* addr);
|
||||
static char* pd_attempt_reserve_memory_at(size_t bytes, char* addr, int file_desc);
|
||||
static void pd_split_reserved_memory(char *base, size_t size,
|
||||
size_t split, bool realloc);
|
||||
static bool pd_commit_memory(char* addr, size_t bytes, bool executable);
|
||||
@ -310,11 +311,11 @@ class os: AllStatic {
|
||||
|
||||
static int vm_allocation_granularity();
|
||||
static char* reserve_memory(size_t bytes, char* addr = 0,
|
||||
size_t alignment_hint = 0);
|
||||
size_t alignment_hint = 0, int file_desc = -1);
|
||||
static char* reserve_memory(size_t bytes, char* addr,
|
||||
size_t alignment_hint, MEMFLAGS flags);
|
||||
static char* reserve_memory_aligned(size_t size, size_t alignment);
|
||||
static char* attempt_reserve_memory_at(size_t bytes, char* addr);
|
||||
static char* reserve_memory_aligned(size_t size, size_t alignment, int file_desc = -1);
|
||||
static char* attempt_reserve_memory_at(size_t bytes, char* addr, int file_desc = -1);
|
||||
static void split_reserved_memory(char *base, size_t size,
|
||||
size_t split, bool realloc);
|
||||
static bool commit_memory(char* addr, size_t bytes, bool executable);
|
||||
@ -345,6 +346,14 @@ class os: AllStatic {
|
||||
static bool create_stack_guard_pages(char* addr, size_t bytes);
|
||||
static bool pd_create_stack_guard_pages(char* addr, size_t bytes);
|
||||
static bool remove_stack_guard_pages(char* addr, size_t bytes);
|
||||
// Helper function to create a new file with template jvmheap.XXXXXX.
|
||||
// Returns a valid fd on success or else returns -1
|
||||
static int create_file_for_heap(const char* dir);
|
||||
// Map memory to the file referred by fd. This function is slightly different from map_memory()
|
||||
// and is added to be used for implementation of -XX:AllocateHeapAt
|
||||
static char* map_memory_to_file(char* base, size_t size, int fd);
|
||||
// Replace existing reserved memory with file mapping
|
||||
static char* replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd);
|
||||
|
||||
static char* map_memory(int fd, const char* file_name, size_t file_offset,
|
||||
char *addr, size_t bytes, bool read_only = false,
|
||||
|
68
test/hotspot/jtreg/gc/TestAllocateHeapAt.java
Normal file
68
test/hotspot/jtreg/gc/TestAllocateHeapAt.java
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 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.
|
||||
*/
|
||||
|
||||
/* @test TestAllocateHeapAt.java
|
||||
* @key gc
|
||||
* @summary Test to check allocation of Java Heap with AllocateHeapAt option
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.misc
|
||||
*/
|
||||
|
||||
import jdk.test.lib.JDKToolFinder;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
public class TestAllocateHeapAt {
|
||||
public static void main(String args[]) throws Exception {
|
||||
ArrayList<String> vmOpts = new ArrayList();
|
||||
|
||||
String testVmOptsStr = System.getProperty("test.java.opts");
|
||||
if (!testVmOptsStr.isEmpty()) {
|
||||
String[] testVmOpts = testVmOptsStr.split(" ");
|
||||
Collections.addAll(vmOpts, testVmOpts);
|
||||
}
|
||||
String test_dir = System.getProperty("test.dir", ".");
|
||||
Collections.addAll(vmOpts, new String[] {"-XX:AllocateHeapAt=" + test_dir,
|
||||
"-Xlog:gc+heap=info",
|
||||
"-Xmx32m",
|
||||
"-Xms32m",
|
||||
"-version"});
|
||||
|
||||
System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java"));
|
||||
for (int i = 0; i < vmOpts.size(); i += 1) {
|
||||
System.out.print(" " + vmOpts.get(i));
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
ProcessBuilder pb =
|
||||
ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()]));
|
||||
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||||
|
||||
System.out.println("Output:\n" + output.getOutput());
|
||||
|
||||
output.shouldContain("Successfully allocated Java heap at location");
|
||||
output.shouldHaveExitValue(0);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user