--- old/src/os/aix/vm/os_aix.cpp 2016-06-03 17:55:41.365733200 -0700 +++ new/src/os/aix/vm/os_aix.cpp 2016-06-03 17:55:41.017655800 -0700 @@ -4900,3 +4900,8 @@ time_t t2 = get_mtime(file2); return t1 - t2; } + +bool os::map_memory_to_file(char* base, size_t size, const char* backingFileDir) { + VMError::report_and_die("Allocating object heap with backing file is not supported for AIX"); + return false; +} \ No newline at end of file --- old/src/os/bsd/vm/os_bsd.cpp 2016-06-03 17:55:43.551023600 -0700 +++ new/src/os/bsd/vm/os_bsd.cpp 2016-06-03 17:55:43.221782400 -0700 @@ -4590,3 +4590,8 @@ } return yes; } + +bool os::map_memory_to_file(char* base, size_t size, const char* backingFileDir) { + VMError::report_and_die("Allocating object heap with backing file is not supported for BSD"); + return false; +} \ No newline at end of file --- old/src/os/linux/vm/os_linux.cpp 2016-06-03 17:55:45.631189500 -0700 +++ new/src/os/linux/vm/os_linux.cpp 2016-06-03 17:55:45.278147600 -0700 @@ -3558,6 +3558,72 @@ return addr; } +// Helper function to create a temp file in the given directory +int os::create_tmpfile(const char* dir, size_t size, bool exec) { + + char name_template[] = "/jvmheap.XXXXXX"; + + char *fullname = (char*)alloca(strlen(dir) + sizeof(name_template)); + (void)strcpy(fullname, dir); + (void)strcat(fullname, name_template); + + sigset_t set, oldset; + sigfillset(&set); + + // block all signals while we do the file operation + (void)sigprocmask(SIG_BLOCK, &set, &oldset); + + // set the file creation mask + mode_t new_mask = exec ? (S_IRUSR | S_IWUSR | S_IXUSR) : (S_IRUSR | S_IWUSR); + mode_t prev_umask = umask(new_mask); + + // create a new file + int fd = mkstemp(fullname); + + // reset the file creation mask + umask(prev_umask); + + if (fd < 0) { + warning("Could not create file for heap"); + return -1; + } + + // delete the name from the filesystem. When 'fd' is closed, the file (and space) will be deleted + (void)unlink(fullname); + + // reset the signal mask + (void)sigprocmask(SIG_SETMASK, &oldset, NULL); + + // allocate space for the file + if ((errno = posix_fallocate(fd, 0, (off_t)size)) != 0) { + warning("Could not allocate sufficient disk space for heap"); + return -1; + } + + return fd; +} + +// Map the given address range to a temporary file created at the specified directory. +// The address range must already be reserved for guaranteed success. If it not reserved, their could be an error while mapping leading to JVM shutdown +bool os::map_memory_to_file(char* base, size_t size, const char* backingFileDir) { + + int fd = os::create_tmpfile(backingFileDir, size, false); + if (fd == -1) { + vm_exit_during_initialization(err_msg("Could not create temporary file in %s for object heap", backingFileDir)); + return false; + } + int prot = PROT_READ | PROT_WRITE; + char* addr = (char*)mmap(base, size, prot, MAP_SHARED | MAP_FIXED, fd, 0); + + if (addr == MAP_FAILED || addr != base) { + close(fd); + vm_exit_during_initialization(err_msg("Error in mapping object heap at the given filesystem dir %s", backingFileDir)); + return false; + } + + return true; +} + // Reserve memory using mmap(MAP_HUGETLB). // - bytes shall be a multiple of alignment. // - req_addr can be NULL. If not NULL, it must be a multiple of alignment. --- old/src/os/solaris/vm/os_solaris.cpp 2016-06-03 17:55:47.725295700 -0700 +++ new/src/os/solaris/vm/os_solaris.cpp 2016-06-03 17:55:47.399268300 -0700 @@ -5862,3 +5862,8 @@ } return yes; } + +bool os::map_memory_to_file(char* base, size_t size, const char* backingFileDir) { + VMError::report_and_die("Allocating object heap with backing file is not supported for Solaris"); + return false; +} \ No newline at end of file --- old/src/os/windows/vm/os_windows.cpp 2016-06-03 17:55:49.815104500 -0700 +++ new/src/os/windows/vm/os_windows.cpp 2016-06-03 17:55:49.475116700 -0700 @@ -3103,6 +3103,67 @@ UseLargePages = success; } +// Helper function to create a temp file in the given directory +int os::create_tmpfile(const char* dir, size_t size, bool exec) { + + char name_template[] = "/jvmheap.XXXXXX"; + + char *fullname = (char*)alloca(strlen(dir) + sizeof(name_template)); + (void)strcpy(fullname, dir); + (void)strcat(fullname, name_template); + os::native_path(fullname); + + char *path = _mktemp(fullname); + if (path == NULL) + return -1; + + int fd = _open(path, O_RDWR | O_CREAT | O_EXCL, S_IWRITE | S_IREAD); + + if (fd < 0) { + warning("Could not create file for heap"); + return -1; + } + + // delete the name from the filesystem. When 'fd' is closed, the file (and space) will be deleted + _unlink(fullname); + + // allocate space for the file + if (_chsize(fd, (long)size) != 0) { + warning("Could not allocate sufficient disk space for heap"); + return -1; + } + + return fd; +} + +// Map the given address range to a temporary file created at the specified directory. +// The address range must already be reserved for guaranteed success. If it not reserved, their could be an error while mapping leading to JVM shutdown +bool os::map_memory_to_file(char* base, size_t size, const char* backingFileDir) { + + int fd = os::create_tmpfile(backingFileDir, size, false); + if (fd == -1) { + vm_exit_during_initialization(err_msg("Could not create temporary file in %s for object heap", backingFileDir)); + return false; + } + + HANDLE fh = (HANDLE)_get_osfhandle(fd); + HANDLE fileMapping = CreateFileMapping(fh, NULL, PAGE_READWRITE, + (DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), NULL); + if (fileMapping == NULL) + return false; + + // release the memory address range and map again at the same address + pd_release_memory(base, size); + LPVOID addr = MapViewOfFileEx(fileMapping, FILE_MAP_WRITE, 0, 0, size, base); + + if (addr == NULL || addr != base) { + CloseHandle(fileMapping); + return false; + } + + return true; +} + // 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. --- old/src/share/vm/memory/universe.cpp 2016-06-03 17:55:51.880225600 -0700 +++ new/src/share/vm/memory/universe.cpp 2016-06-03 17:55:51.532255300 -0700 @@ -812,7 +812,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, FSDirForHeap); if (total_rs.is_reserved()) { assert((total_reserved == total_rs.size()) && ((uintptr_t)total_rs.base() % alignment == 0), --- old/src/share/vm/memory/virtualspace.cpp 2016-06-03 17:55:53.799363600 -0700 +++ new/src/share/vm/memory/virtualspace.cpp 2016-06-03 17:55:53.454367100 -0700 @@ -120,7 +120,9 @@ // 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. - bool special = large && !os::can_commit_large_page_memory(); + // If there is a backing file directory for this VirtualSpace then whether largepages are allocated is upto the filesystem the dir resides in. + // So we ignore the UseLargePages flag in this case. + bool special = (_backingFileDir == NULL) && (large && !os::can_commit_large_page_memory()); char* base = NULL; if (special) { @@ -190,6 +192,13 @@ _base = base; _size = size; _alignment = alignment; + + if (_backingFileDir != NULL) { + // At this point a virtual address range is reserved, now map this memory to a file + os::map_memory_to_file(base, size, _backingFileDir); + // mark this virtual space as _special because the physical memory is committed. + _special = true; + } } @@ -313,7 +322,9 @@ // 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. - bool special = large && !os::can_commit_large_page_memory(); + // If there is a backing file directory for this VirtualSpace then whether largepages are allocated is upto the filesystem the dir resides in. + // So we ignore the UseLargePages flag in this case. + bool special = (_backingFileDir == NULL) && (large && !os::can_commit_large_page_memory()); char* base = NULL; log_trace(gc, heap, coops)("Trying to allocate at address " PTR_FORMAT @@ -366,6 +377,15 @@ if ((((size_t)base) & (alignment - 1)) != 0) { // Base not aligned, retry. release(); + return; + } + if (_backingFileDir != NULL) { + // At this point a virtual address range is reserved, now map this memory to a file + if (!os::map_memory_to_file(base, size, _backingFileDir)) { + vm_exit_during_initialization(err_msg("Error in mapping object heap at the given filesystem dir %s", _backingFileDir)); + } + // mark this virtual space as _special because the physical memory is committed. + _special = true; } } @@ -556,12 +576,13 @@ } } -ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large) : ReservedSpace() { +ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large, const char* backingFSforHeap) : ReservedSpace() { if (size == 0) { return; } + _backingFileDir= backingFSforHeap; // Heap size should be aligned to alignment, too. guarantee(is_size_aligned(size, alignment), "set by caller"); --- old/src/share/vm/memory/virtualspace.hpp 2016-06-03 17:55:55.651479000 -0700 +++ new/src/share/vm/memory/virtualspace.hpp 2016-06-03 17:55:55.338458500 -0700 @@ -37,6 +37,7 @@ size_t _noaccess_prefix; size_t _alignment; bool _special; + const char* _backingFileDir = NULL; private: bool _executable; @@ -111,7 +112,7 @@ 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); + ReservedHeapSpace(size_t size, size_t forced_base_alignment, bool large, const char* backingFSforHeap=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/share/vm/runtime/arguments.cpp 2016-06-03 17:55:57.390547700 -0700 +++ new/src/share/vm/runtime/arguments.cpp 2016-06-03 17:55:57.074550700 -0700 @@ -3265,7 +3265,7 @@ "ManagementServer is not supported in this VM.\n"); return JNI_ERR; #endif // INCLUDE_MANAGEMENT - } else if (match_option(option, "-XX:", &tail)) { // -XX:xxxx + } else if (match_option(option, "-XX:", &tail)) { // -XX:xxxx // Skip -XX:Flags= and -XX:VMOptionsFile= since those cases have // already been handled if ((strncmp(tail, "Flags=", strlen("Flags=")) != 0) && @@ -3275,7 +3275,7 @@ } } // Unknown option - } else if (is_bad_option(option, args->ignoreUnrecognized)) { + } else if (is_bad_option(option, args->ignoreUnrecognized)) { return JNI_ERR; } } @@ -3545,6 +3545,11 @@ return JNI_ERR; } + if (!FLAG_IS_DEFAULT(FSDirForHeap)) { + FLAG_SET_CMDLINE(bool, ForceNUMA, false); + FLAG_SET_CMDLINE(bool, UseNUMA, false); + } + return JNI_OK; } --- old/src/share/vm/runtime/globals.hpp 2016-06-03 17:55:59.403709400 -0700 +++ new/src/share/vm/runtime/globals.hpp 2016-06-03 17:55:59.080670600 -0700 @@ -4157,7 +4157,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, FSDirForHeap, NULL, \ + "Path to the directoy where a temporary file will be created \ + to use as the backing store for Java Heap ") /* --- old/src/share/vm/runtime/os.hpp 2016-06-03 17:56:01.329354600 -0700 +++ new/src/share/vm/runtime/os.hpp 2016-06-03 17:56:01.026289400 -0700 @@ -154,6 +154,7 @@ static void get_summary_cpu_info(char* buf, size_t buflen); static void get_summary_os_info(char* buf, size_t buflen); + static int create_tmpfile(const char* dir, size_t size, bool exec); public: static void init(void); // Called before command line parsing static void init_before_ergo(void); // Called after command line parsing @@ -311,6 +312,10 @@ 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); + + // Map the given address range to a temporary file created at the specified directory. + // The address range must already be reserved for guaranteed success. If it not reserved, their could be an error while mapping leading to JVM shutdown + static bool map_memory_to_file(char* base, size_t size, const char* backing_filename); 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);