8190308: Implementation: JEP 316: Heap Allocation on Alternative Memory Devices
Summary: Sub-task to be used for implementation of JEP 316: Support heap allocation on alternative memory devices
Reviewed-by: sangheki, tschatzl
--- a/src/hotspot/os/aix/os_aix.cpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/os/aix/os_aix.cpp Wed Nov 29 17:03:10 2017 -0800
@@ -2490,6 +2490,22 @@
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) {
--- a/src/hotspot/os/bsd/os_bsd.cpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/os/bsd/os_bsd.cpp Wed Nov 29 17:03:10 2017 -0800
@@ -2350,6 +2350,17 @@
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).
--- a/src/hotspot/os/linux/os_linux.cpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/os/linux/os_linux.cpp Wed Nov 29 17:03:10 2017 -0800
@@ -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 @@
// 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 @@
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 @@
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 @@
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 @@
// initialize thread priority policy
prio_init();
+ if (!FLAG_IS_DEFAULT(AllocateHeapAt)) {
+ set_coredump_filter(false /*largepages*/, true /*dax_shared*/);
+ }
return JNI_OK;
}
--- a/src/hotspot/os/posix/os_posix.cpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/os/posix/os_posix.cpp Wed Nov 29 17:03:10 2017 -0800
@@ -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 @@
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 @@
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 @@
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 @@
}
}
-#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__
--- a/src/hotspot/os/solaris/os_solaris.cpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/os/solaris/os_solaris.cpp Wed Nov 29 17:03:10 2017 -0800
@@ -2585,6 +2585,17 @@
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).
--- a/src/hotspot/os/windows/os_windows.cpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/os/windows/os_windows.cpp Wed Nov 29 17:03:10 2017 -0800
@@ -2904,6 +2904,75 @@
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 @@
// 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* 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);
-
- aligned_base = os::reserve_memory(size, aligned_base);
+ 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, 0, file_desc);
} while (aligned_base == NULL);
@@ -2989,6 +3062,11 @@
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;
}
--- a/src/hotspot/share/memory/universe.cpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/share/memory/universe.cpp Wed Nov 29 17:03:10 2017 -0800
@@ -856,7 +856,7 @@
|| 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 @@
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;
}
--- a/src/hotspot/share/memory/virtualspace.cpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/share/memory/virtualspace.cpp Wed Nov 29 17:03:10 2017 -0800
@@ -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 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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
_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 @@
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 @@
// 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 @@
// 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 @@
_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 @@
}
}
-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 @@
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
--- a/src/hotspot/share/memory/virtualspace.hpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/share/memory/virtualspace.hpp Wed Nov 29 17:03:10 2017 -0800
@@ -37,6 +37,7 @@
size_t _noaccess_prefix;
size_t _alignment;
bool _special;
+ int _fd_for_heap;
private:
bool _executable;
@@ -115,7 +116,9 @@
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; }
--- a/src/hotspot/share/runtime/arguments.cpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/share/runtime/arguments.cpp Wed Nov 29 17:03:10 2017 -0800
@@ -2223,6 +2223,11 @@
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::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);
}
--- a/src/hotspot/share/runtime/globals.hpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/share/runtime/globals.hpp Wed Nov 29 17:03:10 2017 -0800
@@ -4083,7 +4083,11 @@
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.")
/*
--- a/src/hotspot/share/runtime/os.cpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/share/runtime/os.cpp Wed Nov 29 17:03:10 2017 -0800
@@ -1665,10 +1665,21 @@
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 @@
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;
}
--- a/src/hotspot/share/runtime/os.hpp Wed Nov 29 10:30:51 2017 -0800
+++ b/src/hotspot/share/runtime/os.hpp Wed Nov 29 17:03:10 2017 -0800
@@ -108,8 +108,9 @@
}
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 @@
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 @@
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,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/TestAllocateHeapAt.java Wed Nov 29 17:03:10 2017 -0800
@@ -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);
+ }
+}