--- old/src/hotspot/os/aix/os_aix.cpp 2017-11-03 11:29:20.939457801 -0700 +++ new/src/hotspot/os/aix/os_aix.cpp 2017-11-03 11:29:20.670460135 -0700 @@ -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) { --- old/src/hotspot/os/bsd/os_bsd.cpp 2017-11-03 11:29:21.544452551 -0700 +++ new/src/hotspot/os/bsd/os_bsd.cpp 2017-11-03 11:29:21.275454885 -0700 @@ -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). --- old/src/hotspot/os/linux/os_linux.cpp 2017-11-03 11:29:22.181447023 -0700 +++ new/src/hotspot/os/linux/os_linux.cpp 2017-11-03 11:29:21.910449375 -0700 @@ -128,6 +128,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; @@ -3263,10 +3264,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; @@ -3279,8 +3283,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); } @@ -3419,7 +3430,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 @@ -3790,6 +3801,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). @@ -4917,6 +4939,9 @@ // initialize thread priority policy prio_init(); + if (!FLAG_IS_DEFAULT(AllocateHeapAt)) { + set_coredump_filter(false /*largepages*/, true /*dax_shared*/); + } return JNI_OK; } --- old/src/hotspot/os/posix/os_posix.cpp 2017-11-03 11:29:22.852441201 -0700 +++ new/src/hotspot/os/posix/os_posix.cpp 2017-11-03 11:29:22.620443214 -0700 @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -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; } @@ -1351,16 +1500,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__ --- old/src/hotspot/os/solaris/os_solaris.cpp 2017-11-03 11:29:23.477435778 -0700 +++ new/src/hotspot/os/solaris/os_solaris.cpp 2017-11-03 11:29:23.202438164 -0700 @@ -2581,6 +2581,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). --- old/src/hotspot/os/windows/os_windows.cpp 2017-11-03 11:29:24.122430181 -0700 +++ new/src/hotspot/os/windows/os_windows.cpp 2017-11-03 11:29:23.837432654 -0700 @@ -2882,6 +2882,70 @@ 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); + HANDLE fileMapping = CreateFileMapping(fh, NULL, PAGE_READWRITE, + (DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), NULL); + 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. @@ -2901,7 +2965,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"); @@ -2912,16 +2976,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); + 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); @@ -2967,6 +3035,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; } --- old/src/hotspot/share/memory/universe.cpp 2017-11-03 11:29:24.702425148 -0700 +++ new/src/hotspot/share/memory/universe.cpp 2017-11-03 11:29:24.481427065 -0700 @@ -879,7 +879,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), @@ -893,6 +893,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; } --- old/src/hotspot/share/memory/virtualspace.cpp 2017-11-03 11:29:25.296419993 -0700 +++ new/src/hotspot/share/memory/virtualspace.cpp 2017-11-03 11:29:25.020422388 -0700 @@ -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 --- old/src/hotspot/share/memory/virtualspace.hpp 2017-11-03 11:29:25.909414674 -0700 +++ new/src/hotspot/share/memory/virtualspace.hpp 2017-11-03 11:29:25.630417095 -0700 @@ -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; } --- old/src/hotspot/share/runtime/arguments.cpp 2017-11-03 11:29:26.441410058 -0700 +++ new/src/hotspot/share/runtime/arguments.cpp 2017-11-03 11:29:26.216412010 -0700 @@ -2534,6 +2534,11 @@ FLAG_SET_CMDLINE(bool, PostLoopMultiversioning, false); } #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; } @@ -4619,7 +4624,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); } --- old/src/hotspot/share/runtime/globals.hpp 2017-11-03 11:29:27.033404921 -0700 +++ new/src/hotspot/share/runtime/globals.hpp 2017-11-03 11:29:26.806406891 -0700 @@ -4065,7 +4065,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.") /* --- old/src/hotspot/share/runtime/os.cpp 2017-11-03 11:29:27.654399532 -0700 +++ new/src/hotspot/share/runtime/os.cpp 2017-11-03 11:29:27.424401528 -0700 @@ -1674,10 +1674,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; @@ -1694,10 +1705,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; } --- old/src/hotspot/share/runtime/os.hpp 2017-11-03 11:29:28.185394924 -0700 +++ new/src/hotspot/share/runtime/os.hpp 2017-11-03 11:29:27.923397198 -0700 @@ -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); @@ -302,11 +303,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); @@ -337,6 +338,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 2017-10-09 17:27:46.152969745 -0700 +++ new/test/hotspot/jtreg/gc/TestAllocateHeapAt.java 2017-11-03 11:29:28.488392295 -0700 @@ -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 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); + } +}