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:
Kishor Kharbas 2017-11-29 17:03:10 -08:00
parent 3faa620f4c
commit 3fc999a1fe
14 changed files with 503 additions and 54 deletions

@ -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,

@ -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);
}
}