8222480: Implementation: JEP 351: ZGC: Uncommit Unused Memory
authorpliden
Tue, 14 May 2019 09:55:02 +0200
changeset 54834 39ba09047e19
parent 54833 76751d3faf7b
child 54835 43764a3a986d
8222480: Implementation: JEP 351: ZGC: Uncommit Unused Memory Reviewed-by: stefank, eosterlund
src/hotspot/os/linux/gc/z/zNUMA_linux.cpp
src/hotspot/os_cpu/linux_x86/gc/z/zBackingFile_linux_x86.cpp
src/hotspot/os_cpu/linux_x86/gc/z/zBackingFile_linux_x86.hpp
src/hotspot/os_cpu/linux_x86/gc/z/zBackingPath_linux_x86.cpp
src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.cpp
src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.hpp
src/hotspot/share/gc/z/vmStructs_z.hpp
src/hotspot/share/gc/z/zCollectedHeap.cpp
src/hotspot/share/gc/z/zCollectedHeap.hpp
src/hotspot/share/gc/z/zHeap.cpp
src/hotspot/share/gc/z/zHeap.hpp
src/hotspot/share/gc/z/zList.hpp
src/hotspot/share/gc/z/zList.inline.hpp
src/hotspot/share/gc/z/zLiveMap.cpp
src/hotspot/share/gc/z/zLiveMap.hpp
src/hotspot/share/gc/z/zLiveMap.inline.hpp
src/hotspot/share/gc/z/zMemory.cpp
src/hotspot/share/gc/z/zMemory.hpp
src/hotspot/share/gc/z/zPage.cpp
src/hotspot/share/gc/z/zPage.hpp
src/hotspot/share/gc/z/zPage.inline.hpp
src/hotspot/share/gc/z/zPageAllocator.cpp
src/hotspot/share/gc/z/zPageAllocator.hpp
src/hotspot/share/gc/z/zPageCache.cpp
src/hotspot/share/gc/z/zPageCache.hpp
src/hotspot/share/gc/z/zPageCache.inline.hpp
src/hotspot/share/gc/z/zPhysicalMemory.cpp
src/hotspot/share/gc/z/zPhysicalMemory.hpp
src/hotspot/share/gc/z/zPhysicalMemory.inline.hpp
src/hotspot/share/gc/z/zPreMappedMemory.cpp
src/hotspot/share/gc/z/zPreMappedMemory.hpp
src/hotspot/share/gc/z/zPreMappedMemory.inline.hpp
src/hotspot/share/gc/z/zUncommitter.cpp
src/hotspot/share/gc/z/zUncommitter.hpp
src/hotspot/share/gc/z/zVirtualMemory.cpp
src/hotspot/share/gc/z/zVirtualMemory.hpp
src/hotspot/share/gc/z/zVirtualMemory.inline.hpp
src/hotspot/share/gc/z/z_globals.hpp
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/z/ZPageAllocator.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/z/ZPhysicalMemoryManager.java
test/hotspot/gtest/gc/z/test_zForwarding.cpp
test/hotspot/gtest/gc/z/test_zPhysicalMemory.cpp
test/hotspot/gtest/gc/z/test_zVirtualMemory.cpp
test/hotspot/jtreg/ProblemList-zgc.txt
test/hotspot/jtreg/gc/z/TestUncommit.java
--- a/src/hotspot/os/linux/gc/z/zNUMA_linux.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/os/linux/gc/z/zNUMA_linux.cpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -39,7 +39,7 @@
 #endif
 
 static int z_get_mempolicy(uint32_t* mode, const unsigned long *nmask, unsigned long maxnode, uintptr_t addr, int flags) {
-  return syscall(__NR_get_mempolicy, mode, nmask, maxnode, addr, flags);
+  return syscall(SYS_get_mempolicy, mode, nmask, maxnode, addr, flags);
 }
 
 void ZNUMA::initialize_platform() {
--- a/src/hotspot/os_cpu/linux_x86/gc/z/zBackingFile_linux_x86.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/os_cpu/linux_x86/gc/z/zBackingFile_linux_x86.cpp	Tue May 14 09:55:02 2019 +0200
@@ -26,8 +26,10 @@
 #include "gc/z/zBackingFile_linux_x86.hpp"
 #include "gc/z/zBackingPath_linux_x86.hpp"
 #include "gc/z/zErrno.hpp"
+#include "gc/z/zGlobals.hpp"
 #include "gc/z/zLargePages.inline.hpp"
 #include "logging/log.hpp"
+#include "runtime/init.hpp"
 #include "runtime/os.hpp"
 #include "utilities/align.hpp"
 #include "utilities/debug.hpp"
@@ -36,9 +38,54 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/statfs.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
 #include <unistd.h>
 
+//
+// Support for building on older Linux systems
+//
+
+// System calls
+#ifndef SYS_fallocate
+#define SYS_fallocate                    285
+#endif
+#ifndef SYS_memfd_create
+#define SYS_memfd_create                 319
+#endif
+
+// memfd_create(2) flags
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC                      0x0001U
+#endif
+#ifndef MFD_HUGETLB
+#define MFD_HUGETLB                      0x0004U
+#endif
+
+// open(2) flags
+#ifndef O_CLOEXEC
+#define O_CLOEXEC                        02000000
+#endif
+#ifndef O_TMPFILE
+#define O_TMPFILE                        (020000000 | O_DIRECTORY)
+#endif
+
+// fallocate(2) flags
+#ifndef FALLOC_FL_KEEP_SIZE
+#define FALLOC_FL_KEEP_SIZE              0x01
+#endif
+#ifndef FALLOC_FL_PUNCH_HOLE
+#define FALLOC_FL_PUNCH_HOLE             0x02
+#endif
+
+// Filesystem types, see statfs(2)
+#ifndef TMPFS_MAGIC
+#define TMPFS_MAGIC                      0x01021994
+#endif
+#ifndef HUGETLBFS_MAGIC
+#define HUGETLBFS_MAGIC                  0x958458f6
+#endif
+
 // Filesystem names
 #define ZFILESYSTEM_TMPFS                "tmpfs"
 #define ZFILESYSTEM_HUGETLBFS            "hugetlbfs"
@@ -49,31 +96,6 @@
 // Java heap filename
 #define ZFILENAME_HEAP                   "java_heap"
 
-// Support for building on older Linux systems
-#ifndef __NR_memfd_create
-#define __NR_memfd_create                319
-#endif
-#ifndef MFD_CLOEXEC
-#define MFD_CLOEXEC                      0x0001U
-#endif
-#ifndef MFD_HUGETLB
-#define MFD_HUGETLB                      0x0004U
-#endif
-#ifndef O_CLOEXEC
-#define O_CLOEXEC                        02000000
-#endif
-#ifndef O_TMPFILE
-#define O_TMPFILE                        (020000000 | O_DIRECTORY)
-#endif
-
-// Filesystem types, see statfs(2)
-#ifndef TMPFS_MAGIC
-#define TMPFS_MAGIC                      0x01021994
-#endif
-#ifndef HUGETLBFS_MAGIC
-#define HUGETLBFS_MAGIC                  0x958458f6
-#endif
-
 // Preferred tmpfs mount points, ordered by priority
 static const char* z_preferred_tmpfs_mountpoints[] = {
   "/dev/shm",
@@ -88,15 +110,22 @@
   NULL
 };
 
-static int z_memfd_create(const char *name, unsigned int flags) {
-  return syscall(__NR_memfd_create, name, flags);
+static int z_fallocate_hugetlbfs_attempts = 3;
+static bool z_fallocate_supported = true;
+
+static int z_fallocate(int fd, int mode, size_t offset, size_t length) {
+  return syscall(SYS_fallocate, fd, mode, offset, length);
 }
 
-bool ZBackingFile::_hugetlbfs_mmap_retry = true;
+static int z_memfd_create(const char *name, unsigned int flags) {
+  return syscall(SYS_memfd_create, name, flags);
+}
 
 ZBackingFile::ZBackingFile() :
     _fd(-1),
+    _size(0),
     _filesystem(0),
+    _block_size(0),
     _available(0),
     _initialized(false) {
 
@@ -107,46 +136,53 @@
   }
 
   // Get filesystem statistics
-  struct statfs statfs_buf;
-  if (fstatfs(_fd, &statfs_buf) == -1) {
+  struct statfs buf;
+  if (fstatfs(_fd, &buf) == -1) {
     ZErrno err;
-    log_error(gc, init)("Failed to determine filesystem type for backing file (%s)",
-                        err.to_string());
+    log_error(gc)("Failed to determine filesystem type for backing file (%s)", err.to_string());
     return;
   }
 
-  _filesystem = statfs_buf.f_type;
-  _available = statfs_buf.f_bavail * statfs_buf.f_bsize;
+  _filesystem = buf.f_type;
+  _block_size = buf.f_bsize;
+  _available = buf.f_bavail * _block_size;
 
   // Make sure we're on a supported filesystem
   if (!is_tmpfs() && !is_hugetlbfs()) {
-    log_error(gc, init)("Backing file must be located on a %s or a %s filesystem",
-                        ZFILESYSTEM_TMPFS, ZFILESYSTEM_HUGETLBFS);
+    log_error(gc)("Backing file must be located on a %s or a %s filesystem",
+                  ZFILESYSTEM_TMPFS, ZFILESYSTEM_HUGETLBFS);
     return;
   }
 
   // Make sure the filesystem type matches requested large page type
   if (ZLargePages::is_transparent() && !is_tmpfs()) {
-    log_error(gc, init)("-XX:+UseTransparentHugePages can only be enable when using a %s filesystem",
-                        ZFILESYSTEM_TMPFS);
+    log_error(gc)("-XX:+UseTransparentHugePages can only be enable when using a %s filesystem",
+                  ZFILESYSTEM_TMPFS);
     return;
   }
 
   if (ZLargePages::is_transparent() && !tmpfs_supports_transparent_huge_pages()) {
-    log_error(gc, init)("-XX:+UseTransparentHugePages on a %s filesystem not supported by kernel",
-                        ZFILESYSTEM_TMPFS);
+    log_error(gc)("-XX:+UseTransparentHugePages on a %s filesystem not supported by kernel",
+                  ZFILESYSTEM_TMPFS);
     return;
   }
 
   if (ZLargePages::is_explicit() && !is_hugetlbfs()) {
-    log_error(gc, init)("-XX:+UseLargePages (without -XX:+UseTransparentHugePages) can only be enabled when using a %s filesystem",
-                        ZFILESYSTEM_HUGETLBFS);
+    log_error(gc)("-XX:+UseLargePages (without -XX:+UseTransparentHugePages) can only be enabled "
+                  "when using a %s filesystem", ZFILESYSTEM_HUGETLBFS);
     return;
   }
 
   if (!ZLargePages::is_explicit() && is_hugetlbfs()) {
-    log_error(gc, init)("-XX:+UseLargePages must be enabled when using a %s filesystem",
-                        ZFILESYSTEM_HUGETLBFS);
+    log_error(gc)("-XX:+UseLargePages must be enabled when using a %s filesystem",
+                  ZFILESYSTEM_HUGETLBFS);
+    return;
+  }
+
+  const size_t expected_block_size = is_tmpfs() ? os::vm_page_size() : os::large_page_size();
+  if (expected_block_size != _block_size) {
+    log_error(gc)("%s filesystem has unexpected block size " SIZE_FORMAT " (expected " SIZE_FORMAT ")",
+                  is_tmpfs() ? ZFILESYSTEM_TMPFS : ZFILESYSTEM_HUGETLBFS, _block_size, expected_block_size);
     return;
   }
 
@@ -165,7 +201,7 @@
   if (fd == -1) {
     ZErrno err;
     log_debug(gc, init)("Failed to create memfd file (%s)",
-                        ((UseLargePages && err == EINVAL) ? "Hugepages not supported" : err.to_string()));
+                        ((ZLargePages::is_explicit() && err == EINVAL) ? "Hugepages not supported" : err.to_string()));
     return -1;
   }
 
@@ -185,7 +221,7 @@
   // Find mountpoint
   ZBackingPath path(filesystem, preferred_mountpoints);
   if (path.get() == NULL) {
-    log_error(gc, init)("Use -XX:ZPath to specify the path to a %s filesystem", filesystem);
+    log_error(gc)("Use -XX:ZPath to specify the path to a %s filesystem", filesystem);
     return -1;
   }
 
@@ -201,7 +237,7 @@
     struct stat stat_buf;
     if (fstat(fd_anon, &stat_buf) == -1) {
       ZErrno err;
-      log_error(gc, init)("Failed to determine inode number for anonymous file (%s)", err.to_string());
+      log_error(gc)("Failed to determine inode number for anonymous file (%s)", err.to_string());
       return -1;
     }
 
@@ -220,14 +256,14 @@
   const int fd = os::open(filename, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC, S_IRUSR|S_IWUSR);
   if (fd == -1) {
     ZErrno err;
-    log_error(gc, init)("Failed to create file %s (%s)", filename, err.to_string());
+    log_error(gc)("Failed to create file %s (%s)", filename, err.to_string());
     return -1;
   }
 
   // Unlink file
   if (unlink(filename) == -1) {
     ZErrno err;
-    log_error(gc, init)("Failed to unlink file %s (%s)", filename, err.to_string());
+    log_error(gc)("Failed to unlink file %s (%s)", filename, err.to_string());
     return -1;
   }
 
@@ -262,6 +298,10 @@
   return _fd;
 }
 
+size_t ZBackingFile::size() const {
+  return _size;
+}
+
 size_t ZBackingFile::available() const {
   return _available;
 }
@@ -280,147 +320,271 @@
   return access(ZFILENAME_SHMEM_ENABLED, R_OK) == 0;
 }
 
-bool ZBackingFile::try_split_and_expand_tmpfs(size_t offset, size_t length, size_t alignment) const {
-  // Try first smaller part.
-  const size_t offset0 = offset;
-  const size_t length0 = align_up(length / 2, alignment);
-  if (!try_expand_tmpfs(offset0, length0, alignment)) {
-    return false;
+ZErrno ZBackingFile::fallocate_compat_ftruncate(size_t size) const {
+  while (ftruncate(_fd, size) == -1) {
+    if (errno != EINTR) {
+      // Failed
+      return errno;
+    }
   }
 
-  // Try second smaller part.
-  const size_t offset1 = offset0 + length0;
-  const size_t length1 = length - length0;
-  if (!try_expand_tmpfs(offset1, length1, alignment)) {
-    return false;
-  }
-
-  return true;
+  // Success
+  return 0;
 }
 
-bool ZBackingFile::try_expand_tmpfs(size_t offset, size_t length, size_t alignment) const {
-  assert(length > 0, "Invalid length");
-  assert(is_aligned(length, alignment), "Invalid length");
-
-  ZErrno err = posix_fallocate(_fd, offset, length);
-
-  if (err == EINTR && length > alignment) {
-    // Calling posix_fallocate() with a large length can take a long
-    // time to complete. When running profilers, such as VTune, this
-    // syscall will be constantly interrupted by signals. Expanding
-    // the file in smaller steps avoids this problem.
-    return try_split_and_expand_tmpfs(offset, length, alignment);
+ZErrno ZBackingFile::fallocate_compat_mmap(size_t offset, size_t length, bool touch) const {
+  // On hugetlbfs, mapping a file segment will fail immediately, without
+  // the need to touch the mapped pages first, if there aren't enough huge
+  // pages available to back the mapping.
+  void* const addr = mmap(0, length, PROT_READ|PROT_WRITE, MAP_SHARED, _fd, offset);
+  if (addr == MAP_FAILED) {
+    // Failed
+    return errno;
   }
 
-  if (err) {
-    log_error(gc)("Failed to allocate backing file (%s)", err.to_string());
-    return false;
+  // Once mapped, the huge pages are only reserved. We need to touch them
+  // to associate them with the file segment. Note that we can not punch
+  // hole in file segments which only have reserved pages.
+  if (touch) {
+    char* const start = (char*)addr;
+    char* const end = start + length;
+    os::pretouch_memory(start, end, _block_size);
+  }
+
+  // Unmap again. From now on, the huge pages that were mapped are allocated
+  // to this file. There's no risk in getting SIGBUS when touching them.
+  if (munmap(addr, length) == -1) {
+    // Failed
+    return errno;
   }
 
-  return true;
-}
-
-bool ZBackingFile::try_expand_tmpfs(size_t offset, size_t length) const {
-  assert(is_tmpfs(), "Wrong filesystem");
-  return try_expand_tmpfs(offset, length, os::vm_page_size());
+  // Success
+  return 0;
 }
 
-bool ZBackingFile::try_expand_hugetlbfs(size_t offset, size_t length) const {
-  assert(is_hugetlbfs(), "Wrong filesystem");
+ZErrno ZBackingFile::fallocate_compat_pwrite(size_t offset, size_t length) const {
+  uint8_t data = 0;
+
+  // Allocate backing memory by writing to each block
+  for (size_t pos = offset; pos < offset + length; pos += _block_size) {
+    if (pwrite(_fd, &data, sizeof(data), pos) == -1) {
+      // Failed
+      return errno;
+    }
+  }
 
-  // Prior to kernel 4.3, hugetlbfs did not support posix_fallocate().
-  // Instead of posix_fallocate() we can use a well-known workaround,
-  // which involves truncating the file to requested size and then try
-  // to map it to verify that there are enough huge pages available to
-  // back it.
-  while (ftruncate(_fd, offset + length) == -1) {
-    ZErrno err;
-    if (err != EINTR) {
-      log_error(gc)("Failed to truncate backing file (%s)", err.to_string());
-      return false;
+  // Success
+  return 0;
+}
+
+ZErrno ZBackingFile::fallocate_fill_hole_compat(size_t offset, size_t length) {
+  // fallocate(2) is only supported by tmpfs since Linux 3.5, and by hugetlbfs
+  // since Linux 4.3. When fallocate(2) is not supported we emulate it using
+  // ftruncate/pwrite (for tmpfs) or ftruncate/mmap/munmap (for hugetlbfs).
+
+  const size_t end = offset + length;
+  if (end > _size) {
+    // Increase file size
+    const ZErrno err = fallocate_compat_ftruncate(end);
+    if (err) {
+      // Failed
+      return err;
     }
   }
 
-  // If we fail mapping during initialization, i.e. when we are pre-mapping
-  // the heap, then we wait and retry a few times before giving up. Otherwise
-  // there is a risk that running JVMs back-to-back will fail, since there
-  // is a delay between process termination and the huge pages owned by that
-  // process being returned to the huge page pool and made available for new
-  // allocations.
-  void* addr = MAP_FAILED;
-  const int max_attempts = 5;
-  for (int attempt = 1; attempt <= max_attempts; attempt++) {
-    addr = mmap(0, length, PROT_READ|PROT_WRITE, MAP_SHARED, _fd, offset);
-    if (addr != MAP_FAILED || !_hugetlbfs_mmap_retry) {
-      // Mapping was successful or mmap retry is disabled
-      break;
+  // Allocate backing memory
+  const ZErrno err = is_hugetlbfs() ? fallocate_compat_mmap(offset, length, false /* touch */)
+                                    : fallocate_compat_pwrite(offset, length);
+  if (err) {
+    if (end > _size) {
+      // Restore file size
+      fallocate_compat_ftruncate(_size);
     }
 
-    ZErrno err;
-    log_debug(gc)("Failed to map backing file (%s), attempt %d of %d",
-                  err.to_string(), attempt, max_attempts);
+    // Failed
+    return err;
+  }
+
+  if (end > _size) {
+    // Record new file size
+    _size = end;
+  }
+
+  // Success
+  return 0;
+}
+
+ZErrno ZBackingFile::fallocate_fill_hole_syscall(size_t offset, size_t length) {
+  const int mode = 0; // Allocate
+  const int res = z_fallocate(_fd, mode, offset, length);
+  if (res == -1) {
+    // Failed
+    return errno;
+  }
+
+  const size_t end = offset + length;
+  if (end > _size) {
+    // Record new file size
+    _size = end;
+  }
+
+  // Success
+  return 0;
+}
 
-    // Wait and retry in one second, in the hope that
-    // huge pages will be available by then.
-    sleep(1);
+ZErrno ZBackingFile::fallocate_fill_hole(size_t offset, size_t length) {
+  // Using compat mode is more efficient when allocating space on hugetlbfs.
+  // Note that allocating huge pages this way will only reserve them, and not
+  // associate them with segments of the file. We must guarantee that we at
+  // some point touch these segments, otherwise we can not punch hole in them.
+  if (z_fallocate_supported && !is_hugetlbfs()) {
+     const ZErrno err = fallocate_fill_hole_syscall(offset, length);
+     if (!err) {
+       // Success
+       return 0;
+     }
+
+     if (err != ENOSYS && err != EOPNOTSUPP) {
+       // Failed
+       return err;
+     }
+
+     // Not supported
+     log_debug(gc)("Falling back to fallocate() compatibility mode");
+     z_fallocate_supported = false;
+  }
+
+  return fallocate_fill_hole_compat(offset, length);
+}
+
+ZErrno ZBackingFile::fallocate_punch_hole(size_t offset, size_t length) {
+  if (is_hugetlbfs()) {
+    // We can only punch hole in pages that have been touched. Non-touched
+    // pages are only reserved, and not associated with any specific file
+    // segment. We don't know which pages have been previously touched, so
+    // we always touch them here to guarantee that we can punch hole.
+    const ZErrno err = fallocate_compat_mmap(offset, length, true /* touch */);
+    if (err) {
+      // Failed
+      return err;
+    }
   }
 
-  // Disable mmap retry from now on
-  if (_hugetlbfs_mmap_retry) {
-    _hugetlbfs_mmap_retry = false;
+  const int mode = FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE;
+  if (z_fallocate(_fd, mode, offset, length) == -1) {
+    // Failed
+    return errno;
+  }
+
+  // Success
+  return 0;
+}
+
+ZErrno ZBackingFile::split_and_fallocate(bool punch_hole, size_t offset, size_t length) {
+  // Try first half
+  const size_t offset0 = offset;
+  const size_t length0 = align_up(length / 2, _block_size);
+  const ZErrno err0 = fallocate(punch_hole, offset0, length0);
+  if (err0) {
+    return err0;
+  }
+
+  // Try second half
+  const size_t offset1 = offset0 + length0;
+  const size_t length1 = length - length0;
+  const ZErrno err1 = fallocate(punch_hole, offset1, length1);
+  if (err1) {
+    return err1;
   }
 
-  if (addr == MAP_FAILED) {
-    // Not enough huge pages left
-    ZErrno err;
-    log_error(gc)("Failed to map backing file (%s)", err.to_string());
+  // Success
+  return 0;
+}
+
+ZErrno ZBackingFile::fallocate(bool punch_hole, size_t offset, size_t length) {
+  assert(is_aligned(offset, _block_size), "Invalid offset");
+  assert(is_aligned(length, _block_size), "Invalid length");
+
+  const ZErrno err = punch_hole ? fallocate_punch_hole(offset, length) : fallocate_fill_hole(offset, length);
+  if (err == EINTR && length > _block_size) {
+    // Calling fallocate(2) with a large length can take a long time to
+    // complete. When running profilers, such as VTune, this syscall will
+    // be constantly interrupted by signals. Expanding the file in smaller
+    // steps avoids this problem.
+    return split_and_fallocate(punch_hole, offset, length);
+  }
+
+  return err;
+}
+
+bool ZBackingFile::commit_inner(size_t offset, size_t length) {
+  log_trace(gc, heap)("Committing memory: " SIZE_FORMAT "M-" SIZE_FORMAT "M (" SIZE_FORMAT "M)",
+                      offset / M, (offset + length) / M, length / M);
+
+retry:
+  const ZErrno err = fallocate(false /* punch_hole */, offset, length);
+  if (err) {
+    if (err == ENOSPC && !is_init_completed() && is_hugetlbfs() && z_fallocate_hugetlbfs_attempts-- > 0) {
+      // If we fail to allocate during initialization, due to lack of space on
+      // the hugetlbfs filesystem, then we wait and retry a few times before
+      // giving up. Otherwise there is a risk that running JVMs back-to-back
+      // will fail, since there is a delay between process termination and the
+      // huge pages owned by that process being returned to the huge page pool
+      // and made available for new allocations.
+      log_debug(gc, init)("Failed to commit memory (%s), retrying", err.to_string());
+
+      // Wait and retry in one second, in the hope that huge pages will be
+      // available by then.
+      sleep(1);
+      goto retry;
+    }
+
+    // Failed
+    log_error(gc)("Failed to commit memory (%s)", err.to_string());
     return false;
   }
 
-  // Successful mapping, unmap again. From now on the pages we mapped
-  // will be reserved for this file.
-  if (munmap(addr, length) == -1) {
-    ZErrno err;
-    log_error(gc)("Failed to unmap backing file (%s)", err.to_string());
-    return false;
-  }
-
+  // Success
   return true;
 }
 
-bool ZBackingFile::try_expand_tmpfs_or_hugetlbfs(size_t offset, size_t length, size_t alignment) const {
-  assert(is_aligned(offset, alignment), "Invalid offset");
-  assert(is_aligned(length, alignment), "Invalid length");
+size_t ZBackingFile::commit(size_t offset, size_t length) {
+  // Try to commit the whole region
+  if (commit_inner(offset, length)) {
+    // Success
+    return length;
+  }
 
-  log_debug(gc)("Expanding heap from " SIZE_FORMAT "M to " SIZE_FORMAT "M", offset / M, (offset + length) / M);
-
-  return is_hugetlbfs() ? try_expand_hugetlbfs(offset, length) : try_expand_tmpfs(offset, length);
-}
-
-size_t ZBackingFile::try_expand(size_t offset, size_t length, size_t alignment) const {
+  // Failed, try to commit as much as possible
   size_t start = offset;
   size_t end = offset + length;
 
-  // Try to expand
-  if (try_expand_tmpfs_or_hugetlbfs(start, length, alignment)) {
-    // Success
-    return end;
-  }
-
-  // Failed, try to expand as much as possible
   for (;;) {
-    length = align_down((end - start) / 2, alignment);
-    if (length < alignment) {
-      // Done, don't expand more
-      return start;
+    length = align_down((end - start) / 2, ZGranuleSize);
+    if (length < ZGranuleSize) {
+      // Done, don't commit more
+      return start - offset;
     }
 
-    if (try_expand_tmpfs_or_hugetlbfs(start, length, alignment)) {
-      // Success, try expand more
+    if (commit_inner(start, length)) {
+      // Success, try commit more
       start += length;
     } else {
-      // Failed, try expand less
+      // Failed, try commit less
       end -= length;
     }
   }
 }
+
+size_t ZBackingFile::uncommit(size_t offset, size_t length) {
+  log_trace(gc, heap)("Uncommitting memory: " SIZE_FORMAT "M-" SIZE_FORMAT "M (" SIZE_FORMAT "M)",
+                      offset / M, (offset + length) / M, length / M);
+
+  const ZErrno err = fallocate(true /* punch_hole */, offset, length);
+  if (err) {
+    log_error(gc)("Failed to uncommit memory (%s)", err.to_string());
+    return 0;
+  }
+
+  return length;
+}
--- a/src/hotspot/os_cpu/linux_x86/gc/z/zBackingFile_linux_x86.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/os_cpu/linux_x86/gc/z/zBackingFile_linux_x86.hpp	Tue May 14 09:55:02 2019 +0200
@@ -26,12 +26,14 @@
 
 #include "memory/allocation.hpp"
 
+class ZErrno;
+
 class ZBackingFile {
 private:
-  static bool _hugetlbfs_mmap_retry;
-
   int      _fd;
+  size_t   _size;
   uint64_t _filesystem;
+  size_t   _block_size;
   size_t   _available;
   bool     _initialized;
 
@@ -43,11 +45,17 @@
   bool is_hugetlbfs() const;
   bool tmpfs_supports_transparent_huge_pages() const;
 
-  bool try_split_and_expand_tmpfs(size_t offset, size_t length, size_t alignment) const;
-  bool try_expand_tmpfs(size_t offset, size_t length, size_t alignment) const;
-  bool try_expand_tmpfs(size_t offset, size_t length) const;
-  bool try_expand_hugetlbfs(size_t offset, size_t length) const;
-  bool try_expand_tmpfs_or_hugetlbfs(size_t offset, size_t length, size_t alignment) const;
+  ZErrno fallocate_compat_ftruncate(size_t size) const;
+  ZErrno fallocate_compat_mmap(size_t offset, size_t length, bool reserve_only) const;
+  ZErrno fallocate_compat_pwrite(size_t offset, size_t length) const;
+  ZErrno fallocate_fill_hole_compat(size_t offset, size_t length);
+  ZErrno fallocate_fill_hole_syscall(size_t offset, size_t length);
+  ZErrno fallocate_fill_hole(size_t offset, size_t length);
+  ZErrno fallocate_punch_hole(size_t offset, size_t length);
+  ZErrno split_and_fallocate(bool punch_hole, size_t offset, size_t length);
+  ZErrno fallocate(bool punch_hole, size_t offset, size_t length);
+
+  bool commit_inner(size_t offset, size_t length);
 
 public:
   ZBackingFile();
@@ -55,9 +63,11 @@
   bool is_initialized() const;
 
   int fd() const;
+  size_t size() const;
   size_t available() const;
 
-  size_t try_expand(size_t offset, size_t length, size_t alignment) const;
+  size_t commit(size_t offset, size_t length);
+  size_t uncommit(size_t offset, size_t length);
 };
 
 #endif // OS_CPU_LINUX_X86_GC_Z_ZBACKINGFILE_LINUX_X86_HPP
--- a/src/hotspot/os_cpu/linux_x86/gc/z/zBackingPath_linux_x86.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/os_cpu/linux_x86/gc/z/zBackingPath_linux_x86.cpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -72,7 +72,7 @@
   FILE* fd = fopen(PROC_SELF_MOUNTINFO, "r");
   if (fd == NULL) {
     ZErrno err;
-    log_error(gc, init)("Failed to open %s: %s", PROC_SELF_MOUNTINFO, err.to_string());
+    log_error(gc)("Failed to open %s: %s", PROC_SELF_MOUNTINFO, err.to_string());
     return;
   }
 
@@ -113,10 +113,10 @@
   }
 
   // Preferred mount point not found
-  log_error(gc, init)("More than one %s filesystem found:", filesystem);
+  log_error(gc)("More than one %s filesystem found:", filesystem);
   ZArrayIterator<char*> iter2(mountpoints);
   for (char* mountpoint; iter2.next(&mountpoint);) {
-    log_error(gc, init)("  %s", mountpoint);
+    log_error(gc)("  %s", mountpoint);
   }
 
   return NULL;
@@ -130,7 +130,7 @@
 
   if (mountpoints.size() == 0) {
     // No mount point found
-    log_error(gc, init)("Failed to find an accessible %s filesystem", filesystem);
+    log_error(gc)("Failed to find an accessible %s filesystem", filesystem);
   } else if (mountpoints.size() == 1) {
     // One mount point found
     path = strdup(mountpoints.at(0));
--- a/src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.cpp	Tue May 14 09:55:02 2019 +0200
@@ -32,6 +32,7 @@
 #include "gc/z/zPhysicalMemory.inline.hpp"
 #include "gc/z/zPhysicalMemoryBacking_linux_x86.hpp"
 #include "logging/log.hpp"
+#include "runtime/init.hpp"
 #include "runtime/os.hpp"
 #include "utilities/align.hpp"
 #include "utilities/debug.hpp"
@@ -40,7 +41,11 @@
 #include <sys/mman.h>
 #include <sys/types.h>
 
+//
 // Support for building on older Linux systems
+//
+
+// madvise(2) flags
 #ifndef MADV_HUGEPAGE
 #define MADV_HUGEPAGE                        14
 #endif
@@ -48,22 +53,37 @@
 // Proc file entry for max map mount
 #define ZFILENAME_PROC_MAX_MAP_COUNT         "/proc/sys/vm/max_map_count"
 
-ZPhysicalMemoryBacking::ZPhysicalMemoryBacking(size_t max_capacity) :
-    _manager(),
-    _file() {
+bool ZPhysicalMemoryBacking::is_initialized() const {
+  return _file.is_initialized();
+}
 
-  if (!_file.is_initialized()) {
+void ZPhysicalMemoryBacking::warn_available_space(size_t max) const {
+  // Note that the available space on a tmpfs or a hugetlbfs filesystem
+  // will be zero if no size limit was specified when it was mounted.
+  const size_t available = _file.available();
+  if (available == 0) {
+    // No size limit set, skip check
+    log_info(gc, init)("Available space on backing filesystem: N/A");
     return;
   }
 
-  // Check and warn if max map count is too low
-  check_max_map_count(max_capacity);
+  log_info(gc, init)("Available space on backing filesystem: " SIZE_FORMAT "M", available / M);
 
-  // Check and warn if available space on filesystem is too low
-  check_available_space_on_filesystem(max_capacity);
+  // Warn if the filesystem doesn't currently have enough space available to hold
+  // the max heap size. The max heap size will be capped if we later hit this limit
+  // when trying to expand the heap.
+  if (available < max) {
+    log_warning(gc)("***** WARNING! INCORRECT SYSTEM CONFIGURATION DETECTED! *****");
+    log_warning(gc)("Not enough space available on the backing filesystem to hold the current max Java heap");
+    log_warning(gc)("size (" SIZE_FORMAT "M). Please adjust the size of the backing filesystem accordingly "
+                    "(available", max / M);
+    log_warning(gc)("space is currently " SIZE_FORMAT "M). Continuing execution with the current filesystem "
+                    "size could", available / M);
+    log_warning(gc)("lead to a premature OutOfMemoryError being thrown, due to failure to map memory.");
+  }
 }
 
-void ZPhysicalMemoryBacking::check_max_map_count(size_t max_capacity) const {
+void ZPhysicalMemoryBacking::warn_max_map_count(size_t max) const {
   const char* const filename = ZFILENAME_PROC_MAX_MAP_COUNT;
   FILE* const file = fopen(filename, "r");
   if (file == NULL) {
@@ -86,62 +106,101 @@
   // However, ZGC tends to create the most mappings and dominate the total count.
   // In the worst cases, ZGC will map each granule three times, i.e. once per heap view.
   // We speculate that we need another 20% to allow for non-ZGC subsystems to map memory.
-  const size_t required_max_map_count = (max_capacity / ZGranuleSize) * 3 * 1.2;
+  const size_t required_max_map_count = (max / ZGranuleSize) * 3 * 1.2;
   if (actual_max_map_count < required_max_map_count) {
-    log_warning(gc, init)("***** WARNING! INCORRECT SYSTEM CONFIGURATION DETECTED! *****");
-    log_warning(gc, init)("The system limit on number of memory mappings per process might be too low "
-                          "for the given");
-    log_warning(gc, init)("max Java heap size (" SIZE_FORMAT "M). Please adjust %s to allow for at",
-                          max_capacity / M, filename);
-    log_warning(gc, init)("least " SIZE_FORMAT " mappings (current limit is " SIZE_FORMAT "). Continuing "
-                          "execution with the current", required_max_map_count, actual_max_map_count);
-    log_warning(gc, init)("limit could lead to a fatal error, due to failure to map memory.");
+    log_warning(gc)("***** WARNING! INCORRECT SYSTEM CONFIGURATION DETECTED! *****");
+    log_warning(gc)("The system limit on number of memory mappings per process might be too low for the given");
+    log_warning(gc)("max Java heap size (" SIZE_FORMAT "M). Please adjust %s to allow for at",
+                    max / M, filename);
+    log_warning(gc)("least " SIZE_FORMAT " mappings (current limit is " SIZE_FORMAT "). Continuing execution "
+                    "with the current", required_max_map_count, actual_max_map_count);
+    log_warning(gc)("limit could lead to a fatal error, due to failure to map memory.");
   }
 }
 
-void ZPhysicalMemoryBacking::check_available_space_on_filesystem(size_t max_capacity) const {
-  // Note that the available space on a tmpfs or a hugetlbfs filesystem
-  // will be zero if no size limit was specified when it was mounted.
-  const size_t available = _file.available();
-  if (available == 0) {
-    // No size limit set, skip check
-    log_info(gc, init)("Available space on backing filesystem: N/A");
-    return;
+void ZPhysicalMemoryBacking::warn_commit_limits(size_t max) const {
+  // Warn if available space is too low
+  warn_available_space(max);
+
+  // Warn if max map count is too low
+  warn_max_map_count(max);
+}
+
+bool ZPhysicalMemoryBacking::supports_uncommit() {
+  assert(!is_init_completed(), "Invalid state");
+  assert(_file.size() >= ZGranuleSize, "Invalid size");
+
+  // Test if uncommit is supported by uncommitting and then re-committing a granule
+  return commit(uncommit(ZGranuleSize)) == ZGranuleSize;
+}
+
+size_t ZPhysicalMemoryBacking::commit(size_t size) {
+  size_t committed = 0;
+
+  // Fill holes in the backing file
+  while (committed < size) {
+    size_t allocated = 0;
+    const size_t remaining = size - committed;
+    const uintptr_t start = _uncommitted.alloc_from_front_at_most(remaining, &allocated);
+    if (start == UINTPTR_MAX) {
+      // No holes to commit
+      break;
+    }
+
+    // Try commit hole
+    const size_t filled = _file.commit(start, allocated);
+    if (filled > 0) {
+      // Successful or partialy successful
+      _committed.free(start, filled);
+      committed += filled;
+    }
+    if (filled < allocated) {
+      // Failed or partialy failed
+      _uncommitted.free(start + filled, allocated - filled);
+      return committed;
+    }
   }
 
-  log_info(gc, init)("Available space on backing filesystem: " SIZE_FORMAT "M",
-                     available / M);
+  // Expand backing file
+  if (committed < size) {
+    const size_t remaining = size - committed;
+    const uintptr_t start = _file.size();
+    const size_t expanded = _file.commit(start, remaining);
+    if (expanded > 0) {
+      // Successful or partialy successful
+      _committed.free(start, expanded);
+      committed += expanded;
+    }
+  }
 
-  // Warn if the filesystem doesn't currently have enough space available to hold
-  // the max heap size. The max heap size will be capped if we later hit this limit
-  // when trying to expand the heap.
-  if (available < max_capacity) {
-    log_warning(gc, init)("***** WARNING! INCORRECT SYSTEM CONFIGURATION DETECTED! *****");
-    log_warning(gc, init)("Not enough space available on the backing filesystem to hold the current "
-                          "max Java heap");
-    log_warning(gc, init)("size (" SIZE_FORMAT "M). Please adjust the size of the backing filesystem "
-                          "accordingly (available", max_capacity / M);
-    log_warning(gc, init)("space is currently " SIZE_FORMAT "M). Continuing execution with the current "
-                          "filesystem size could", available / M);
-    log_warning(gc, init)("lead to a premature OutOfMemoryError being thrown, due to failure to map "
-                          "memory.");
-  }
+  return committed;
 }
 
-bool ZPhysicalMemoryBacking::is_initialized() const {
-  return _file.is_initialized();
-}
+size_t ZPhysicalMemoryBacking::uncommit(size_t size) {
+  size_t uncommitted = 0;
+
+  // Punch holes in backing file
+  while (uncommitted < size) {
+    size_t allocated = 0;
+    const size_t remaining = size - uncommitted;
+    const uintptr_t start = _committed.alloc_from_back_at_most(remaining, &allocated);
+    assert(start != UINTPTR_MAX, "Allocation should never fail");
 
-size_t ZPhysicalMemoryBacking::try_expand(size_t old_capacity, size_t new_capacity) {
-  assert(old_capacity < new_capacity, "Invalid old/new capacity");
-
-  const size_t capacity = _file.try_expand(old_capacity, new_capacity - old_capacity, ZGranuleSize);
-  if (capacity > old_capacity) {
-    // Add expanded capacity to free list
-    _manager.free(old_capacity, capacity - old_capacity);
+    // Try punch hole
+    const size_t punched = _file.uncommit(start, allocated);
+    if (punched > 0) {
+      // Successful or partialy successful
+      _uncommitted.free(start, punched);
+      uncommitted += punched;
+    }
+    if (punched < allocated) {
+      // Failed or partialy failed
+      _committed.free(start + punched, allocated - punched);
+      return uncommitted;
+    }
   }
 
-  return capacity;
+  return uncommitted;
 }
 
 ZPhysicalMemory ZPhysicalMemoryBacking::alloc(size_t size) {
@@ -151,7 +210,7 @@
 
   // Allocate segments
   for (size_t allocated = 0; allocated < size; allocated += ZGranuleSize) {
-    const uintptr_t start = _manager.alloc_from_front(ZGranuleSize);
+    const uintptr_t start = _committed.alloc_from_front(ZGranuleSize);
     assert(start != UINTPTR_MAX, "Allocation should never fail");
     pmem.add_segment(ZPhysicalMemorySegment(start, ZGranuleSize));
   }
@@ -159,13 +218,13 @@
   return pmem;
 }
 
-void ZPhysicalMemoryBacking::free(ZPhysicalMemory pmem) {
+void ZPhysicalMemoryBacking::free(const ZPhysicalMemory& pmem) {
   const size_t nsegments = pmem.nsegments();
 
   // Free segments
   for (size_t i = 0; i < nsegments; i++) {
-    const ZPhysicalMemorySegment segment = pmem.segment(i);
-    _manager.free(segment.start(), segment.size());
+    const ZPhysicalMemorySegment& segment = pmem.segment(i);
+    _committed.free(segment.start(), segment.size());
   }
 }
 
@@ -178,10 +237,10 @@
   }
 }
 
-void ZPhysicalMemoryBacking::advise_view(uintptr_t addr, size_t size) const {
-  if (madvise((void*)addr, size, MADV_HUGEPAGE) == -1) {
+void ZPhysicalMemoryBacking::advise_view(uintptr_t addr, size_t size, int advice) const {
+  if (madvise((void*)addr, size, advice) == -1) {
     ZErrno err;
-    log_error(gc)("Failed to advise use of transparent huge pages (%s)", err.to_string());
+    log_error(gc)("Failed to advise on memory (advice %d, %s)", advice, err.to_string());
   }
 }
 
@@ -190,41 +249,42 @@
   os::pretouch_memory((void*)addr, (void*)(addr + size), page_size);
 }
 
-void ZPhysicalMemoryBacking::map_view(ZPhysicalMemory pmem, uintptr_t addr, bool pretouch) const {
+void ZPhysicalMemoryBacking::map_view(const ZPhysicalMemory& pmem, uintptr_t addr, bool pretouch) const {
   const size_t nsegments = pmem.nsegments();
+  size_t size = 0;
 
   // Map segments
   for (size_t i = 0; i < nsegments; i++) {
-    const ZPhysicalMemorySegment segment = pmem.segment(i);
-    const size_t size = segment.size();
-    const void* const res = mmap((void*)addr, size, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, _file.fd(), segment.start());
+    const ZPhysicalMemorySegment& segment = pmem.segment(i);
+    const uintptr_t segment_addr = addr + size;
+    const void* const res = mmap((void*)segment_addr, segment.size(), PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, _file.fd(), segment.start());
     if (res == MAP_FAILED) {
       ZErrno err;
       map_failed(err);
     }
 
-    // Advise on use of transparent huge pages before touching it
-    if (ZLargePages::is_transparent()) {
-      advise_view(addr, size);
-    }
+    size += segment.size();
+  }
 
-    // NUMA interleave memory before touching it
-    ZNUMA::memory_interleave(addr, size);
+  // Advise on use of transparent huge pages before touching it
+  if (ZLargePages::is_transparent()) {
+    advise_view(addr, size, MADV_HUGEPAGE);
+  }
 
-    if (pretouch) {
-      pretouch_view(addr, size);
-    }
+  // NUMA interleave memory before touching it
+  ZNUMA::memory_interleave(addr, size);
 
-    addr += size;
+  // Pre-touch memory
+  if (pretouch) {
+    pretouch_view(addr, size);
   }
 }
 
-void ZPhysicalMemoryBacking::unmap_view(ZPhysicalMemory pmem, uintptr_t addr) const {
+void ZPhysicalMemoryBacking::unmap_view(const ZPhysicalMemory& pmem, uintptr_t addr) const {
   // Note that we must keep the address space reservation intact and just detach
   // the backing memory. For this reason we map a new anonymous, non-accessible
   // and non-reserved page over the mapping instead of actually unmapping.
-  const size_t size = pmem.size();
-  const void* const res = mmap((void*)addr, size, PROT_NONE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
+  const void* const res = mmap((void*)addr, pmem.size(), PROT_NONE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
   if (res == MAP_FAILED) {
     ZErrno err;
     map_failed(err);
@@ -232,11 +292,11 @@
 }
 
 uintptr_t ZPhysicalMemoryBacking::nmt_address(uintptr_t offset) const {
-  // From an NMT point of view we treat the first heap mapping (marked0) as committed
+  // From an NMT point of view we treat the first heap view (marked0) as committed
   return ZAddress::marked0(offset);
 }
 
-void ZPhysicalMemoryBacking::map(ZPhysicalMemory pmem, uintptr_t offset) const {
+void ZPhysicalMemoryBacking::map(const ZPhysicalMemory& pmem, uintptr_t offset) const {
   if (ZVerifyViews) {
     // Map good view
     map_view(pmem, ZAddress::good(offset), AlwaysPreTouch);
@@ -248,7 +308,7 @@
   }
 }
 
-void ZPhysicalMemoryBacking::unmap(ZPhysicalMemory pmem, uintptr_t offset) const {
+void ZPhysicalMemoryBacking::unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const {
   if (ZVerifyViews) {
     // Unmap good view
     unmap_view(pmem, ZAddress::good(offset));
@@ -260,13 +320,13 @@
   }
 }
 
-void ZPhysicalMemoryBacking::debug_map(ZPhysicalMemory pmem, uintptr_t offset) const {
+void ZPhysicalMemoryBacking::debug_map(const ZPhysicalMemory& pmem, uintptr_t offset) const {
   // Map good view
   assert(ZVerifyViews, "Should be enabled");
   map_view(pmem, ZAddress::good(offset), false /* pretouch */);
 }
 
-void ZPhysicalMemoryBacking::debug_unmap(ZPhysicalMemory pmem, uintptr_t offset) const {
+void ZPhysicalMemoryBacking::debug_unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const {
   // Unmap good view
   assert(ZVerifyViews, "Should be enabled");
   unmap_view(pmem, ZAddress::good(offset));
--- a/src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.hpp	Tue May 14 09:55:02 2019 +0200
@@ -32,35 +32,39 @@
 
 class ZPhysicalMemoryBacking {
 private:
-  ZMemoryManager _manager;
   ZBackingFile   _file;
+  ZMemoryManager _committed;
+  ZMemoryManager _uncommitted;
 
-  void check_max_map_count(size_t max_capacity) const;
-  void check_available_space_on_filesystem(size_t max_capacity) const;
+  void warn_available_space(size_t max) const;
+  void warn_max_map_count(size_t max) const;
+
   void map_failed(ZErrno err) const;
 
-  void advise_view(uintptr_t addr, size_t size) const;
+  void advise_view(uintptr_t addr, size_t size, int advice) const;
   void pretouch_view(uintptr_t addr, size_t size) const;
-  void map_view(ZPhysicalMemory pmem, uintptr_t addr, bool pretouch) const;
-  void unmap_view(ZPhysicalMemory pmem, uintptr_t addr) const;
+  void map_view(const ZPhysicalMemory& pmem, uintptr_t addr, bool pretouch) const;
+  void unmap_view(const ZPhysicalMemory& pmem, uintptr_t addr) const;
 
 public:
-  ZPhysicalMemoryBacking(size_t max_capacity);
-
   bool is_initialized() const;
 
-  size_t try_expand(size_t old_capacity, size_t new_capacity);
+  void warn_commit_limits(size_t max) const;
+  bool supports_uncommit();
+
+  size_t commit(size_t size);
+  size_t uncommit(size_t size);
 
   ZPhysicalMemory alloc(size_t size);
-  void free(ZPhysicalMemory pmem);
+  void free(const ZPhysicalMemory& pmem);
 
   uintptr_t nmt_address(uintptr_t offset) const;
 
-  void map(ZPhysicalMemory pmem, uintptr_t offset) const;
-  void unmap(ZPhysicalMemory pmem, uintptr_t offset) const;
+  void map(const ZPhysicalMemory& pmem, uintptr_t offset) const;
+  void unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const;
 
-  void debug_map(ZPhysicalMemory pmem, uintptr_t offset) const;
-  void debug_unmap(ZPhysicalMemory pmem, uintptr_t offset) const;
+  void debug_map(const ZPhysicalMemory& pmem, uintptr_t offset) const;
+  void debug_unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const;
 };
 
 #endif // OS_CPU_LINUX_X86_GC_Z_ZPHYSICALMEMORYBACKING_LINUX_X86_HPP
--- a/src/hotspot/share/gc/z/vmStructs_z.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/vmStructs_z.hpp	Tue May 14 09:55:02 2019 +0200
@@ -30,7 +30,6 @@
 #include "gc/z/zGranuleMap.hpp"
 #include "gc/z/zHeap.hpp"
 #include "gc/z/zPageAllocator.hpp"
-#include "gc/z/zPhysicalMemory.hpp"
 #include "utilities/macros.hpp"
 
 // Expose some ZGC globals to the SA agent.
@@ -77,20 +76,18 @@
   nonstatic_field(ZPage,                        _virtual,             const ZVirtualMemory)          \
   volatile_nonstatic_field(ZPage,               _top,                 uintptr_t)                     \
                                                                                                      \
-  nonstatic_field(ZPageAllocator,               _physical,            ZPhysicalMemoryManager)        \
+  nonstatic_field(ZPageAllocator,               _max_capacity,        const size_t)                  \
+  nonstatic_field(ZPageAllocator,               _capacity,            size_t)                        \
   nonstatic_field(ZPageAllocator,               _used,                size_t)                        \
                                                                                                      \
   nonstatic_field(ZPageTable,                   _map,                 ZGranuleMapForPageTable)       \
                                                                                                      \
   nonstatic_field(ZGranuleMapForPageTable,      _map,                 ZPage** const)                 \
                                                                                                      \
-  nonstatic_field(ZVirtualMemory,               _start,               uintptr_t)                     \
-  nonstatic_field(ZVirtualMemory,               _end,                 uintptr_t)                     \
+  nonstatic_field(ZVirtualMemory,               _start,               const uintptr_t)               \
+  nonstatic_field(ZVirtualMemory,               _end,                 const uintptr_t)               \
                                                                                                      \
-  nonstatic_field(ZForwarding,                  _entries,             const ZAttachedArrayForForwarding) \
-                                                                                                     \
-  nonstatic_field(ZPhysicalMemoryManager,       _max_capacity,        const size_t)                  \
-  nonstatic_field(ZPhysicalMemoryManager,       _capacity,            size_t)
+  nonstatic_field(ZForwarding,                  _entries,             const ZAttachedArrayForForwarding)
 
 #define VM_INT_CONSTANTS_ZGC(declare_constant, declare_constant_with_value)                          \
   declare_constant(ZPhaseRelocate)                                                                   \
--- a/src/hotspot/share/gc/z/zCollectedHeap.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp	Tue May 14 09:55:02 2019 +0200
@@ -48,6 +48,7 @@
     _heap(),
     _director(new ZDirector()),
     _driver(new ZDriver()),
+    _uncommitter(new ZUncommitter()),
     _stat(new ZStat()),
     _runtime_workers() {}
 
@@ -77,6 +78,7 @@
 void ZCollectedHeap::stop() {
   _director->stop();
   _driver->stop();
+  _uncommitter->stop();
   _stat->stop();
 }
 
@@ -272,6 +274,7 @@
 void ZCollectedHeap::gc_threads_do(ThreadClosure* tc) const {
   tc->do_thread(_director);
   tc->do_thread(_driver);
+  tc->do_thread(_uncommitter);
   tc->do_thread(_stat);
   _heap.worker_threads_do(tc);
   _runtime_workers.threads_do(tc);
@@ -331,6 +334,8 @@
   st->cr();
   _driver->print_on(st);
   st->cr();
+  _uncommitter->print_on(st);
+  st->cr();
   _stat->print_on(st);
   st->cr();
   _heap.print_worker_threads_on(st);
--- a/src/hotspot/share/gc/z/zCollectedHeap.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zCollectedHeap.hpp	Tue May 14 09:55:02 2019 +0200
@@ -29,10 +29,11 @@
 #include "gc/z/zBarrierSet.hpp"
 #include "gc/z/zDirector.hpp"
 #include "gc/z/zDriver.hpp"
+#include "gc/z/zHeap.hpp"
 #include "gc/z/zInitialize.hpp"
-#include "gc/z/zHeap.hpp"
 #include "gc/z/zRuntimeWorkers.hpp"
 #include "gc/z/zStat.hpp"
+#include "gc/z/zUncommitter.hpp"
 
 class ZCollectedHeap : public CollectedHeap {
   friend class VMStructs;
@@ -44,6 +45,7 @@
   ZHeap             _heap;
   ZDirector*        _director;
   ZDriver*          _driver;
+  ZUncommitter*     _uncommitter;
   ZStat*            _stat;
   ZRuntimeWorkers   _runtime_workers;
 
--- a/src/hotspot/share/gc/z/zHeap.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zHeap.cpp	Tue May 14 09:55:02 2019 +0200
@@ -22,6 +22,7 @@
  */
 
 #include "precompiled.hpp"
+#include "gc/shared/gcArguments.hpp"
 #include "gc/shared/oopStorage.hpp"
 #include "gc/z/zAddress.hpp"
 #include "gc/z/zGlobals.hpp"
@@ -45,6 +46,7 @@
 #include "logging/log.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/oop.inline.hpp"
+#include "runtime/arguments.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/thread.hpp"
 #include "utilities/align.hpp"
@@ -62,7 +64,7 @@
 ZHeap::ZHeap() :
     _workers(),
     _object_allocator(_workers.nworkers()),
-    _page_allocator(heap_min_size(), heap_max_size(), heap_max_reserve_size()),
+    _page_allocator(heap_min_size(), heap_initial_size(), heap_max_size(), heap_max_reserve_size()),
     _page_table(),
     _forwarding_table(),
     _mark(&_workers, &_page_table),
@@ -81,13 +83,15 @@
 }
 
 size_t ZHeap::heap_min_size() const {
-  const size_t aligned_min_size = align_up(InitialHeapSize, ZGranuleSize);
-  return MIN2(aligned_min_size, heap_max_size());
+  return MinHeapSize;
+}
+
+size_t ZHeap::heap_initial_size() const {
+  return InitialHeapSize;
 }
 
 size_t ZHeap::heap_max_size() const {
-  const size_t aligned_max_size = align_up(MaxHeapSize, ZGranuleSize);
-  return MIN2(aligned_max_size, ZAddressOffsetMax);
+  return MaxHeapSize;
 }
 
 size_t ZHeap::heap_max_reserve_size() const {
@@ -102,7 +106,7 @@
 }
 
 size_t ZHeap::min_capacity() const {
-  return heap_min_size();
+  return _page_allocator.min_capacity();
 }
 
 size_t ZHeap::max_capacity() const {
@@ -250,10 +254,14 @@
   _page_allocator.free_page(page, reclaimed);
 }
 
+uint64_t ZHeap::uncommit(uint64_t delay) {
+  return _page_allocator.uncommit(delay);
+}
+
 void ZHeap::before_flip() {
   if (ZVerifyViews) {
     // Unmap all pages
-    _page_allocator.unmap_all_pages();
+    _page_allocator.debug_unmap_all_pages();
   }
 }
 
@@ -262,8 +270,9 @@
     // Map all pages
     ZPageTableIterator iter(&_page_table);
     for (ZPage* page; iter.next(&page);) {
-      _page_allocator.map_page(page);
+      _page_allocator.debug_map_page(page);
     }
+    _page_allocator.debug_map_cached_pages();
   }
 }
 
--- a/src/hotspot/share/gc/z/zHeap.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zHeap.hpp	Tue May 14 09:55:02 2019 +0200
@@ -66,6 +66,7 @@
   ZServiceability     _serviceability;
 
   size_t heap_min_size() const;
+  size_t heap_initial_size() const;
   size_t heap_max_size() const;
   size_t heap_max_reserve_size() const;
 
@@ -129,6 +130,9 @@
   void undo_alloc_page(ZPage* page);
   void free_page(ZPage* page, bool reclaimed);
 
+  // Uncommit memory
+  uint64_t uncommit(uint64_t delay);
+
   // Object allocation
   uintptr_t alloc_tlab(size_t size);
   uintptr_t alloc_object(size_t size);
--- a/src/hotspot/share/gc/z/zList.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zList.hpp	Tue May 14 09:55:02 2019 +0200
@@ -210,11 +210,11 @@
 template <typename T, bool forward>
 class ZListIteratorImpl : public StackObj {
 private:
-  ZList<T>* const _list;
-  T*              _next;
+  const ZList<T>* const _list;
+  T*                    _next;
 
 public:
-  ZListIteratorImpl(ZList<T>* list);
+  ZListIteratorImpl(const ZList<T>* list);
 
   bool next(T** elem);
 };
@@ -226,14 +226,14 @@
 template <typename T>
 class ZListIterator : public ZListIteratorImpl<T, ZLIST_FORWARD> {
 public:
-  ZListIterator(ZList<T>* list) :
+  ZListIterator(const ZList<T>* list) :
       ZListIteratorImpl<T, ZLIST_FORWARD>(list) {}
 };
 
 template <typename T>
 class ZListReverseIterator : public ZListIteratorImpl<T, ZLIST_REVERSE> {
 public:
-  ZListReverseIterator(ZList<T>* list) :
+  ZListReverseIterator(const ZList<T>* list) :
       ZListIteratorImpl<T, ZLIST_REVERSE>(list) {}
 };
 
--- a/src/hotspot/share/gc/z/zList.inline.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zList.inline.hpp	Tue May 14 09:55:02 2019 +0200
@@ -27,7 +27,7 @@
 #include "gc/z/zList.hpp"
 
 template <typename T, bool forward>
-ZListIteratorImpl<T, forward>::ZListIteratorImpl(ZList<T>* list) :
+ZListIteratorImpl<T, forward>::ZListIteratorImpl(const ZList<T>* list) :
     _list(list),
     _next(forward ? list->first() : list->last()) {}
 
--- a/src/hotspot/share/gc/z/zLiveMap.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zLiveMap.cpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -34,15 +34,19 @@
 static const ZStatCounter ZCounterMarkSeqNumResetContention("Contention", "Mark SeqNum Reset Contention", ZStatUnitOpsPerSecond);
 static const ZStatCounter ZCounterMarkSegmentResetContention("Contention", "Mark Segment Reset Contention", ZStatUnitOpsPerSecond);
 
+static size_t bitmap_size(uint32_t size, size_t nsegments) {
+  // We need at least one bit per segment
+  return MAX2<size_t>(size, nsegments) * 2;
+}
+
 ZLiveMap::ZLiveMap(uint32_t size) :
     _seqnum(0),
     _live_objects(0),
     _live_bytes(0),
     _segment_live_bits(0),
     _segment_claim_bits(0),
-    // We need at least one bit per segment.
-    _bitmap(MAX2<size_t>(size, nsegments) * 2),
-    _shift(exact_log2(segment_size())) {}
+    _bitmap(bitmap_size(size, nsegments)),
+    _segment_shift(exact_log2(segment_size())) {}
 
 void ZLiveMap::reset(size_t index) {
   const uint32_t seqnum_initializing = (uint32_t)-1;
@@ -121,3 +125,11 @@
   const bool success = set_segment_live_atomic(segment);
   assert(success, "Should never fail");
 }
+
+void ZLiveMap::resize(uint32_t size) {
+  const size_t new_bitmap_size = bitmap_size(size, nsegments);
+  if (_bitmap.size() != new_bitmap_size) {
+    _bitmap.reinitialize(new_bitmap_size, false /* clear */);
+    _segment_shift = exact_log2(segment_size());
+  }
+}
--- a/src/hotspot/share/gc/z/zLiveMap.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zLiveMap.hpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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,13 +35,13 @@
 private:
   static const size_t nsegments = 64;
 
-  volatile uint32_t _seqnum;              // Mark sequence number
-  volatile uint32_t _live_objects;        // Number of live objects
-  volatile size_t   _live_bytes;          // Number of live bytes
-  BitMap::bm_word_t _segment_live_bits;   // Segment live bits
-  BitMap::bm_word_t _segment_claim_bits;  // Segment claim bits
-  ZBitMap           _bitmap;              // Mark bitmap
-  const size_t      _shift;               // Segment shift
+  volatile uint32_t _seqnum;
+  volatile uint32_t _live_objects;
+  volatile size_t   _live_bytes;
+  BitMap::bm_word_t _segment_live_bits;
+  BitMap::bm_word_t _segment_claim_bits;
+  ZBitMap           _bitmap;
+  size_t            _segment_shift;
 
   const BitMapView segment_live_bits() const;
   const BitMapView segment_claim_bits() const;
@@ -72,6 +72,7 @@
   ZLiveMap(uint32_t size);
 
   void reset();
+  void resize(uint32_t size);
 
   bool is_marked() const;
 
--- a/src/hotspot/share/gc/z/zLiveMap.inline.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zLiveMap.inline.hpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -92,7 +92,7 @@
 }
 
 inline BitMap::idx_t ZLiveMap::index_to_segment(BitMap::idx_t index) const {
-  return index >> _shift;
+  return index >> _segment_shift;
 }
 
 inline bool ZLiveMap::get(size_t index) const {
--- a/src/hotspot/share/gc/z/zMemory.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zMemory.cpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -49,6 +49,30 @@
   return UINTPTR_MAX;
 }
 
+uintptr_t ZMemoryManager::alloc_from_front_at_most(size_t size, size_t* allocated) {
+  ZMemory* area = _freelist.first();
+  if (area != NULL) {
+    if (area->size() <= size) {
+      // Smaller than or equal to requested, remove area
+      const uintptr_t start = area->start();
+      *allocated = area->size();
+      _freelist.remove(area);
+      delete area;
+      return start;
+    } else {
+      // Larger than requested, shrink area
+      const uintptr_t start = area->start();
+      area->shrink_from_front(size);
+      *allocated = size;
+      return start;
+    }
+  }
+
+  // Out of memory
+  *allocated = 0;
+  return UINTPTR_MAX;
+}
+
 uintptr_t ZMemoryManager::alloc_from_back(size_t size) {
   ZListReverseIterator<ZMemory> iter(&_freelist);
   for (ZMemory* area; iter.next(&area);) {
@@ -71,6 +95,29 @@
   return UINTPTR_MAX;
 }
 
+uintptr_t ZMemoryManager::alloc_from_back_at_most(size_t size, size_t* allocated) {
+  ZMemory* area = _freelist.last();
+  if (area != NULL) {
+    if (area->size() <= size) {
+      // Smaller than or equal to requested, remove area
+      const uintptr_t start = area->start();
+      *allocated = area->size();
+      _freelist.remove(area);
+      delete area;
+      return start;
+    } else {
+      // Larger than requested, shrink area
+      area->shrink_from_back(size);
+      *allocated = size;
+      return area->end();
+    }
+  }
+
+  // Out of memory
+  *allocated = 0;
+  return UINTPTR_MAX;
+}
+
 void ZMemoryManager::free(uintptr_t start, size_t size) {
   assert(start != UINTPTR_MAX, "Invalid address");
   const uintptr_t end = start + size;
--- a/src/hotspot/share/gc/z/zMemory.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zMemory.hpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -54,7 +54,9 @@
 
 public:
   uintptr_t alloc_from_front(size_t size);
+  uintptr_t alloc_from_front_at_most(size_t size, size_t* allocated);
   uintptr_t alloc_from_back(size_t size);
+  uintptr_t alloc_from_back_at_most(size_t size, size_t* allocated);
   void free(uintptr_t start, size_t size);
 };
 
--- a/src/hotspot/share/gc/z/zPage.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPage.cpp	Tue May 14 09:55:02 2019 +0200
@@ -28,30 +28,72 @@
 #include "utilities/align.hpp"
 #include "utilities/debug.hpp"
 
-ZPage::ZPage(uint8_t type, ZVirtualMemory vmem, ZPhysicalMemory pmem) :
+ZPage::ZPage(const ZVirtualMemory& vmem, const ZPhysicalMemory& pmem) :
+    _type(type_from_size(vmem.size())),
+    _numa_id((uint8_t)-1),
+    _seqnum(0),
+    _virtual(vmem),
+    _top(start()),
+    _livemap(object_max_count()),
+    _last_used(0),
+    _physical(pmem) {
+  assert_initialized();
+}
+
+ZPage::ZPage(uint8_t type, const ZVirtualMemory& vmem, const ZPhysicalMemory& pmem) :
     _type(type),
     _numa_id((uint8_t)-1),
     _seqnum(0),
     _virtual(vmem),
     _top(start()),
     _livemap(object_max_count()),
+    _last_used(0),
     _physical(pmem) {
-  assert(!_physical.is_null(), "Should not be null");
-  assert(!_virtual.is_null(), "Should not be null");
-  assert((type == ZPageTypeSmall && size() == ZPageSizeSmall) ||
-         (type == ZPageTypeMedium && size() == ZPageSizeMedium) ||
-         (type == ZPageTypeLarge && is_aligned(size(), ZGranuleSize)),
-         "Page type/size mismatch");
+  assert_initialized();
 }
 
-ZPage::~ZPage() {
-  assert(_physical.is_null(), "Should be null");
+void ZPage::assert_initialized() const {
+  assert(!_virtual.is_null(), "Should not be null");
+  assert(!_physical.is_null(), "Should not be null");
+  assert((_type == ZPageTypeSmall && size() == ZPageSizeSmall) ||
+         (_type == ZPageTypeMedium && size() == ZPageSizeMedium) ||
+         (_type == ZPageTypeLarge && is_aligned(size(), ZGranuleSize)),
+         "Page type/size mismatch");
 }
 
 void ZPage::reset() {
   _seqnum = ZGlobalSeqNum;
   _top = start();
   _livemap.reset();
+  _last_used = 0;
+}
+
+ZPage* ZPage::retype(uint8_t type) {
+  assert(_type != type, "Invalid retype");
+  _type = type;
+  _livemap.resize(object_max_count());
+  return this;
+}
+
+ZPage* ZPage::split(size_t size) {
+  return split(type_from_size(size), size);
+}
+
+ZPage* ZPage::split(uint8_t type, size_t size) {
+  assert(_virtual.size() > size, "Invalid split");
+
+  // Resize this page, keep _numa_id, _seqnum, and _last_used
+  const ZVirtualMemory vmem = _virtual.split(size);
+  const ZPhysicalMemory pmem = _physical.split(size);
+  _type = type_from_size(_virtual.size());
+  _top = start();
+  _livemap.resize(object_max_count());
+
+  // Create new page, inherit _seqnum and _last_used
+  ZPage* const page = new ZPage(type, vmem, pmem);
+  page->_seqnum = _seqnum;
+  page->_last_used = _last_used;
+  return page;
 }
 
 void ZPage::print_on(outputStream* out) const {
--- a/src/hotspot/share/gc/z/zPage.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPage.hpp	Tue May 14 09:55:02 2019 +0200
@@ -35,26 +35,27 @@
   friend class ZList<ZPage>;
 
 private:
-  // Always hot
-  const uint8_t        _type;             // Page type
-  uint8_t              _numa_id;          // NUMA node affinity
-  uint32_t             _seqnum;           // Allocation sequence number
-  const ZVirtualMemory _virtual;          // Virtual start/end address
-  volatile uintptr_t   _top;              // Virtual top address
-  ZLiveMap             _livemap;          // Live map
+  uint8_t            _type;
+  uint8_t            _numa_id;
+  uint32_t           _seqnum;
+  ZVirtualMemory     _virtual;
+  volatile uintptr_t _top;
+  ZLiveMap           _livemap;
+  uint64_t           _last_used;
+  ZPhysicalMemory    _physical;
+  ZListNode<ZPage>   _node;
 
-  // Hot when relocated and cached
-  ZPhysicalMemory      _physical;         // Physical memory for page
-  ZListNode<ZPage>     _node;             // Page list node
+  void assert_initialized() const;
 
+  uint8_t type_from_size(size_t size) const;
   const char* type_to_string() const;
 
   bool is_object_marked(uintptr_t addr) const;
   bool is_object_strongly_marked(uintptr_t addr) const;
 
 public:
-  ZPage(uint8_t type, ZVirtualMemory vmem, ZPhysicalMemory pmem);
-  ~ZPage();
+  ZPage(const ZVirtualMemory& vmem, const ZPhysicalMemory& pmem);
+  ZPage(uint8_t type, const ZVirtualMemory& vmem, const ZPhysicalMemory& pmem);
 
   uint32_t object_max_count() const;
   size_t object_alignment_shift() const;
@@ -67,17 +68,10 @@
   uintptr_t top() const;
   size_t remaining() const;
 
-  uint8_t numa_id();
-
-  ZPhysicalMemory& physical_memory();
+  const ZPhysicalMemory& physical_memory() const;
   const ZVirtualMemory& virtual_memory() const;
 
-  void reset();
-
-  bool is_in(uintptr_t addr) const;
-
-  uintptr_t block_start(uintptr_t addr) const;
-  bool block_is_obj(uintptr_t addr) const;
+  uint8_t numa_id();
 
   bool is_allocating() const;
   bool is_relocatable() const;
@@ -85,6 +79,20 @@
   bool is_mapped() const;
   void set_pre_mapped();
 
+  uint64_t last_used() const;
+  void set_last_used();
+
+  void reset();
+
+  ZPage* retype(uint8_t type);
+  ZPage* split(size_t size);
+  ZPage* split(uint8_t type, size_t size);
+
+  bool is_in(uintptr_t addr) const;
+
+  uintptr_t block_start(uintptr_t addr) const;
+  bool block_is_obj(uintptr_t addr) const;
+
   bool is_marked() const;
   bool is_object_live(uintptr_t addr) const;
   bool is_object_strongly_live(uintptr_t addr) const;
--- a/src/hotspot/share/gc/z/zPage.inline.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPage.inline.hpp	Tue May 14 09:55:02 2019 +0200
@@ -34,10 +34,23 @@
 #include "gc/z/zVirtualMemory.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/atomic.hpp"
-#include "runtime/orderAccess.hpp"
+#include "runtime/os.hpp"
 #include "utilities/align.hpp"
 #include "utilities/debug.hpp"
 
+inline uint8_t ZPage::type_from_size(size_t size) const {
+  switch (size) {
+  case ZPageSizeSmall:
+    return ZPageTypeSmall;
+
+  case ZPageSizeMedium:
+    return ZPageTypeMedium;
+
+  default:
+    return ZPageTypeLarge;
+  }
+}
+
 inline const char* ZPage::type_to_string() const {
   switch (type()) {
   case ZPageTypeSmall:
@@ -116,7 +129,7 @@
   return end() - top();
 }
 
-inline ZPhysicalMemory& ZPage::physical_memory() {
+inline const ZPhysicalMemory& ZPage::physical_memory() const {
   return _physical;
 }
 
@@ -132,23 +145,6 @@
   return _numa_id;
 }
 
-inline bool ZPage::is_in(uintptr_t addr) const {
-  const uintptr_t offset = ZAddress::offset(addr);
-  return offset >= start() && offset < top();
-}
-
-inline uintptr_t ZPage::block_start(uintptr_t addr) const {
-  if (block_is_obj(addr)) {
-    return addr;
-  } else {
-    return ZAddress::good(top());
-  }
-}
-
-inline bool ZPage::block_is_obj(uintptr_t addr) const {
-  return ZAddress::offset(addr) < top();
-}
-
 inline bool ZPage::is_allocating() const {
   return _seqnum == ZGlobalSeqNum;
 }
@@ -168,6 +164,31 @@
   _seqnum = 1;
 }
 
+inline uint64_t ZPage::last_used() const {
+  return _last_used;
+}
+
+inline void ZPage::set_last_used() {
+  _last_used = os::elapsedTime();
+}
+
+inline bool ZPage::is_in(uintptr_t addr) const {
+  const uintptr_t offset = ZAddress::offset(addr);
+  return offset >= start() && offset < top();
+}
+
+inline uintptr_t ZPage::block_start(uintptr_t addr) const {
+  if (block_is_obj(addr)) {
+    return addr;
+  } else {
+    return ZAddress::good(top());
+  }
+}
+
+inline bool ZPage::block_is_obj(uintptr_t addr) const {
+  return ZAddress::offset(addr) < top();
+}
+
 inline bool ZPage::is_marked() const {
   assert(is_relocatable(), "Invalid page state");
   return _livemap.is_marked();
--- a/src/hotspot/share/gc/z/zPageAllocator.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPageAllocator.cpp	Tue May 14 09:55:02 2019 +0200
@@ -22,6 +22,7 @@
  */
 
 #include "precompiled.hpp"
+#include "gc/shared/suspendibleThreadSet.hpp"
 #include "gc/z/zAddress.inline.hpp"
 #include "gc/z/zCollectedHeap.hpp"
 #include "gc/z/zFuture.inline.hpp"
@@ -30,14 +31,16 @@
 #include "gc/z/zPage.inline.hpp"
 #include "gc/z/zPageAllocator.hpp"
 #include "gc/z/zPageCache.inline.hpp"
-#include "gc/z/zPreMappedMemory.inline.hpp"
 #include "gc/z/zSafeDelete.inline.hpp"
 #include "gc/z/zStat.hpp"
 #include "gc/z/zTracer.inline.hpp"
 #include "runtime/init.hpp"
+#include "runtime/java.hpp"
+#include "utilities/debug.hpp"
 
 static const ZStatCounter       ZCounterAllocationRate("Memory", "Allocation Rate", ZStatUnitBytesPerSecond);
-static const ZStatCounter       ZCounterPageCacheEvict("Memory", "Page Cache Evict", ZStatUnitBytesPerSecond);
+static const ZStatCounter       ZCounterPageCacheFlush("Memory", "Page Cache Flush", ZStatUnitBytesPerSecond);
+static const ZStatCounter       ZCounterUncommit("Memory", "Uncommit", ZStatUnitBytesPerSecond);
 static const ZStatCriticalPhase ZCriticalPhaseAllocationStall("Allocation Stall");
 
 class ZPageAllocRequest : public StackObj {
@@ -85,37 +88,105 @@
 
 ZPage* const ZPageAllocator::gc_marker = (ZPage*)-1;
 
-ZPageAllocator::ZPageAllocator(size_t min_capacity, size_t max_capacity, size_t max_reserve) :
+ZPageAllocator::ZPageAllocator(size_t min_capacity,
+                               size_t initial_capacity,
+                               size_t max_capacity,
+                               size_t max_reserve) :
     _lock(),
     _virtual(),
-    _physical(max_capacity),
+    _physical(),
     _cache(),
+    _min_capacity(min_capacity),
+    _max_capacity(max_capacity),
     _max_reserve(max_reserve),
-    _pre_mapped(_virtual, _physical, try_ensure_unused_for_pre_mapped(min_capacity)),
+    _current_max_capacity(max_capacity),
+    _capacity(0),
     _used_high(0),
     _used_low(0),
     _used(0),
     _allocated(0),
     _reclaimed(0),
     _queue(),
-    _safe_delete() {}
+    _safe_delete(),
+    _uncommit(false),
+    _initialized(false) {
+
+  if (!_virtual.is_initialized() || !_physical.is_initialized()) {
+    return;
+  }
+
+  log_info(gc, init)("Min Capacity: " SIZE_FORMAT "M", min_capacity / M);
+  log_info(gc, init)("Initial Capacity: " SIZE_FORMAT "M", initial_capacity / M);
+  log_info(gc, init)("Max Capacity: " SIZE_FORMAT "M", max_capacity / M);
+  log_info(gc, init)("Max Reserve: " SIZE_FORMAT "M", max_reserve / M);
+  log_info(gc, init)("Pre-touch: %s", AlwaysPreTouch ? "Enabled" : "Disabled");
+
+  // Warn if system limits could stop us from reaching max capacity
+  _physical.warn_commit_limits(max_capacity);
+
+  // Commit initial capacity
+  _capacity = _physical.commit(initial_capacity);
+  if (_capacity != initial_capacity) {
+    log_error(gc)("Failed to allocate initial Java heap (" SIZE_FORMAT "M)", initial_capacity / M);
+    return;
+  }
+
+  // If uncommit is not explicitly disabled, max capacity is greater than
+  // min capacity, and uncommit is supported by the platform, then we will
+  // try to uncommit unused memory.
+  _uncommit = ZUncommit && (max_capacity > min_capacity) && _physical.supports_uncommit();
+  if (_uncommit) {
+    log_info(gc, init)("Uncommit: Enabled, Delay: " UINTX_FORMAT "s", ZUncommitDelay);
+  } else {
+    log_info(gc, init)("Uncommit: Disabled");
+  }
+
+  // Pre-map initial capacity
+  prime_cache(initial_capacity);
+
+  // Successfully initialized
+  _initialized = true;
+}
+
+void ZPageAllocator::prime_cache(size_t size) {
+  // Allocate physical memory
+  const ZPhysicalMemory pmem = _physical.alloc(size);
+  guarantee(!pmem.is_null(), "Invalid size");
+
+  // Allocate virtual memory
+  const ZVirtualMemory vmem = _virtual.alloc(size, true /* alloc_from_front */);
+  guarantee(!vmem.is_null(), "Invalid size");
+
+  // Allocate page
+  ZPage* const page = new ZPage(vmem, pmem);
+
+  // Map page
+  map_page(page);
+  page->set_pre_mapped();
+
+  // Add page to cache
+  page->set_last_used();
+  _cache.free_page(page);
+}
 
 bool ZPageAllocator::is_initialized() const {
-  return _physical.is_initialized() &&
-         _virtual.is_initialized() &&
-         _pre_mapped.is_initialized();
+  return _initialized;
+}
+
+size_t ZPageAllocator::min_capacity() const {
+  return _min_capacity;
 }
 
 size_t ZPageAllocator::max_capacity() const {
-  return _physical.max_capacity();
+  return _max_capacity;
 }
 
 size_t ZPageAllocator::current_max_capacity() const {
-  return _physical.current_max_capacity();
+  return _current_max_capacity;
 }
 
 size_t ZPageAllocator::capacity() const {
-  return _physical.capacity();
+  return _capacity;
 }
 
 size_t ZPageAllocator::max_reserve() const {
@@ -135,7 +206,7 @@
 }
 
 size_t ZPageAllocator::unused() const {
-  const ssize_t unused = (ssize_t)_physical.capacity() - (ssize_t)_used - (ssize_t)_max_reserve;
+  const ssize_t unused = (ssize_t)_capacity - (ssize_t)_used - (ssize_t)_max_reserve;
   return unused > 0 ? (size_t)unused : 0;
 }
 
@@ -181,83 +252,40 @@
   }
 }
 
-size_t ZPageAllocator::max_available(bool no_reserve) const {
-  size_t available = current_max_capacity() - used();
-
-  if (no_reserve) {
-    // The reserve should not be considered available
-    available -= MIN2(available, max_reserve());
-  }
-
-  return available;
-}
-
-size_t ZPageAllocator::try_ensure_unused(size_t size, bool no_reserve) {
-  // Ensure that we always have space available for the reserve. This
-  // is needed to avoid losing the reserve because of failure to map
-  // more memory before reaching max capacity.
-  _physical.try_ensure_unused_capacity(size + max_reserve());
-
-  size_t unused = _physical.unused_capacity();
-
-  if (no_reserve) {
-    // The reserve should not be considered unused
-    unused -= MIN2(unused, max_reserve());
-  }
-
-  return MIN2(size, unused);
-}
-
-size_t ZPageAllocator::try_ensure_unused_for_pre_mapped(size_t size) {
-  // This function is called during construction, where the
-  // physical memory manager might have failed to initialied.
-  if (!_physical.is_initialized()) {
-    return 0;
-  }
-
-  return try_ensure_unused(size, true /* no_reserve */);
-}
-
 ZPage* ZPageAllocator::create_page(uint8_t type, size_t size) {
-  // Allocate physical memory
-  const ZPhysicalMemory pmem = _physical.alloc(size);
-  if (pmem.is_null()) {
-    // Out of memory
-    return NULL;
-  }
-
   // Allocate virtual memory
   const ZVirtualMemory vmem = _virtual.alloc(size);
   if (vmem.is_null()) {
     // Out of address space
-    _physical.free(pmem);
     return NULL;
   }
 
+  // Allocate physical memory
+  const ZPhysicalMemory pmem = _physical.alloc(size);
+  assert(!pmem.is_null(), "Invalid size");
+
   // Allocate page
   return new ZPage(type, vmem, pmem);
 }
 
-void ZPageAllocator::flush_pre_mapped() {
-  if (_pre_mapped.available() == 0) {
-    return;
-  }
+void ZPageAllocator::destroy_page(ZPage* page) {
+  const ZVirtualMemory& vmem = page->virtual_memory();
+  const ZPhysicalMemory& pmem = page->physical_memory();
 
-  // Detach the memory mapping.
-  detach_memory(_pre_mapped.virtual_memory(), _pre_mapped.physical_memory());
+  // Unmap memory
+  _physical.unmap(pmem, vmem.start());
 
-  _pre_mapped.clear();
-}
+  // Free physical memory
+  _physical.free(pmem);
 
-void ZPageAllocator::destroy_page(ZPage* page) {
-  // Detach virtual and physical memory
-  detach_memory(page->virtual_memory(), page->physical_memory());
+  // Free virtual memory
+  _virtual.free(vmem);
 
   // Delete page safely
   _safe_delete(page);
 }
 
-void ZPageAllocator::map_page(ZPage* page) {
+void ZPageAllocator::map_page(const ZPage* page) const {
   // Map physical memory
   if (!page->is_mapped()) {
     _physical.map(page->physical_memory(), page->start());
@@ -266,57 +294,92 @@
   }
 }
 
-void ZPageAllocator::unmap_all_pages() {
-  ZPhysicalMemory pmem(ZPhysicalMemorySegment(0 /* start */, ZAddressOffsetMax));
-  _physical.debug_unmap(pmem, 0 /* offset */);
-  pmem.clear();
+size_t ZPageAllocator::max_available(bool no_reserve) const {
+  size_t available = _current_max_capacity - _used;
+
+  if (no_reserve) {
+    // The reserve should not be considered available
+    available -= MIN2(available, _max_reserve);
+  }
+
+  return available;
 }
 
-void ZPageAllocator::check_out_of_memory_during_initialization() {
-  if (!is_init_completed()) {
-    vm_exit_during_initialization("java.lang.OutOfMemoryError", "Java heap too small");
+bool ZPageAllocator::ensure_available(size_t size, bool no_reserve) {
+  if (max_available(no_reserve) < size) {
+    // Not enough free memory
+    return false;
+  }
+
+  // We add the max_reserve to the requested size to avoid losing
+  // the reserve because of failure to increase capacity before
+  // reaching max capacity.
+  size += _max_reserve;
+
+  // Don't try to increase capacity if enough unused capacity
+  // is available or if current max capacity has been reached.
+  const size_t available = _capacity - _used;
+  if (available < size && _capacity < _current_max_capacity) {
+    // Try to increase capacity
+    const size_t commit = MIN2(size - available, _current_max_capacity - _capacity);
+    const size_t committed = _physical.commit(commit);
+    _capacity += committed;
+
+    log_trace(gc, heap)("Make Available: Size: " SIZE_FORMAT "M, NoReserve: %s, "
+                        "Available: " SIZE_FORMAT "M, Commit: " SIZE_FORMAT "M, "
+                        "Committed: " SIZE_FORMAT "M, Capacity: " SIZE_FORMAT "M",
+                        size / M, no_reserve ? "True" : "False", available / M,
+                        commit / M, committed / M, _capacity / M);
+
+    if (committed != commit) {
+      // Failed, or partly failed, to increase capacity. Adjust current
+      // max capacity to avoid further attempts to increase capacity.
+      log_error(gc)("Forced to lower max Java heap size from "
+                    SIZE_FORMAT "M(%.0lf%%) to " SIZE_FORMAT "M(%.0lf%%)",
+                    _current_max_capacity / M, percent_of(_current_max_capacity, _max_capacity),
+                    _capacity / M, percent_of(_capacity, _max_capacity));
+
+      _current_max_capacity = _capacity;
+    }
+  }
+
+  if (!no_reserve) {
+    size -= _max_reserve;
+  }
+
+  const size_t new_available = _capacity - _used;
+  return new_available >= size;
+}
+
+void ZPageAllocator::ensure_uncached_available(size_t size) {
+  assert(_capacity - _used >= size, "Invalid size");
+  const size_t uncached_available = _capacity - _used - _cache.available();
+  if (size > uncached_available) {
+    flush_cache_for_allocation(size - uncached_available);
   }
 }
 
-ZPage* ZPageAllocator::alloc_page_common_inner(uint8_t type, size_t size, ZAllocationFlags flags) {
-  const size_t max = max_available(flags.no_reserve());
-  if (max < size) {
+ZPage* ZPageAllocator::alloc_page_common_inner(uint8_t type, size_t size, bool no_reserve) {
+  if (!ensure_available(size, no_reserve)) {
     // Not enough free memory
     return NULL;
   }
 
-  // Try allocating from the page cache
-  ZPage* const cached_page = _cache.alloc_page(type, size);
-  if (cached_page != NULL) {
-    return cached_page;
-  }
-
-  // Try allocate from the pre-mapped memory
-  ZPage* const pre_mapped_page = _pre_mapped.alloc_page(type, size);
-  if (pre_mapped_page != NULL) {
-    return pre_mapped_page;
+  // Try allocate page from the cache
+  ZPage* const page = _cache.alloc_page(type, size);
+  if (page != NULL) {
+    return page;
   }
 
-  // Flush any remaining pre-mapped memory so that
-  // subsequent allocations can use the physical memory.
-  flush_pre_mapped();
+  // Try flush pages from the cache
+  ensure_uncached_available(size);
 
-  // Try ensure that physical memory is available
-  const size_t unused = try_ensure_unused(size, flags.no_reserve());
-  if (unused < size) {
-    // Try evict pages from the cache
-    const size_t needed = size - unused;
-    if (_cache.available() >= needed) {
-      evict_cache(needed);
-    }
-  }
-
-  // Create new page and allocate physical memory
+  // Create new page
   return create_page(type, size);
 }
 
 ZPage* ZPageAllocator::alloc_page_common(uint8_t type, size_t size, ZAllocationFlags flags) {
-  ZPage* const page = alloc_page_common_inner(type, size, flags);
+  ZPage* const page = alloc_page_common_inner(type, size, flags.no_reserve());
   if (page == NULL) {
     // Out of memory
     return NULL;
@@ -326,11 +389,17 @@
   increase_used(size, flags.relocation());
 
   // Send trace event
-  ZTracer::tracer()->report_page_alloc(size, used(), max_available(flags.no_reserve()), _cache.available(), flags);
+  ZTracer::tracer()->report_page_alloc(size, _used, max_available(flags.no_reserve()), _cache.available(), flags);
 
   return page;
 }
 
+void ZPageAllocator::check_out_of_memory_during_initialization() {
+  if (!is_init_completed()) {
+    vm_exit_during_initialization("java.lang.OutOfMemoryError", "Java heap too small");
+  }
+}
+
 ZPage* ZPageAllocator::alloc_page_blocking(uint8_t type, size_t size, ZAllocationFlags flags) {
   // Prepare to block
   ZPageAllocRequest request(type, size, flags, ZCollectedHeap::heap()->total_collections());
@@ -433,28 +502,15 @@
   }
 }
 
-void ZPageAllocator::detach_memory(const ZVirtualMemory& vmem, ZPhysicalMemory& pmem) {
-  const uintptr_t addr = vmem.start();
-
-  // Free virtual memory
-  _virtual.free(vmem);
-
-  // Unmap physical memory
-  _physical.unmap(pmem, addr);
-
-  // Free physical memory
-  _physical.free(pmem);
-
-  // Clear physical mapping
-  pmem.clear();
-}
-
 void ZPageAllocator::free_page(ZPage* page, bool reclaimed) {
   ZLocker<ZLock> locker(&_lock);
 
   // Update used statistics
   decrease_used(page->size(), reclaimed);
 
+  // Set time when last used
+  page->set_last_used();
+
   // Cache page
   _cache.free_page(page);
 
@@ -462,59 +518,157 @@
   satisfy_alloc_queue();
 }
 
-void ZPageAllocator::flush_cache(ZPageCacheFlushClosure* cl) {
+size_t ZPageAllocator::flush_cache(ZPageCacheFlushClosure* cl) {
   ZList<ZPage> list;
 
+  // Flush pages
   _cache.flush(cl, &list);
 
+  const size_t overflushed = cl->overflushed();
+  if (overflushed > 0) {
+    // Overflushed, keep part of last page
+    ZPage* const page = list.last()->split(overflushed);
+    _cache.free_page(page);
+  }
+
+  // Destroy pages
+  size_t flushed = 0;
   for (ZPage* page = list.remove_first(); page != NULL; page = list.remove_first()) {
+    flushed += page->size();
     destroy_page(page);
   }
+
+  return flushed;
 }
 
-class ZPageCacheEvictClosure : public ZPageCacheFlushClosure {
-private:
-  const size_t _requested;
-  size_t       _evicted;
-
+class ZPageCacheFlushForAllocationClosure : public ZPageCacheFlushClosure {
 public:
-  ZPageCacheEvictClosure(size_t requested) :
-      _requested(requested),
-      _evicted(0) {}
+  ZPageCacheFlushForAllocationClosure(size_t requested) :
+      ZPageCacheFlushClosure(requested) {}
 
   virtual bool do_page(const ZPage* page) {
-    if (_evicted < _requested) {
-      // Evict page
-      _evicted += page->size();
+    if (_flushed < _requested) {
+      // Flush page
+      _flushed += page->size();
       return true;
     }
 
-    // Don't evict page
+    // Don't flush page
     return false;
   }
-
-  size_t evicted() const {
-    return _evicted;
-  }
 };
 
-void ZPageAllocator::evict_cache(size_t requested) {
-  // Evict pages
-  ZPageCacheEvictClosure cl(requested);
-  flush_cache(&cl);
+void ZPageAllocator::flush_cache_for_allocation(size_t requested) {
+  assert(requested <= _cache.available(), "Invalid request");
 
-  const size_t evicted = cl.evicted();
+  // Flush pages
+  ZPageCacheFlushForAllocationClosure cl(requested);
+  const size_t flushed = flush_cache(&cl);
+
+  assert(requested == flushed, "Failed to flush");
+
   const size_t cached_after = _cache.available();
-  const size_t cached_before = cached_after + evicted;
+  const size_t cached_before = cached_after + flushed;
 
   log_info(gc, heap)("Page Cache: " SIZE_FORMAT "M(%.0lf%%)->" SIZE_FORMAT "M(%.0lf%%), "
-                     "Evicted: " SIZE_FORMAT "M, Requested: " SIZE_FORMAT "M",
+                     "Flushed: " SIZE_FORMAT "M",
                      cached_before / M, percent_of(cached_before, max_capacity()),
                      cached_after / M, percent_of(cached_after, max_capacity()),
-                     evicted / M, requested / M);
+                     flushed / M);
 
   // Update statistics
-  ZStatInc(ZCounterPageCacheEvict, evicted);
+  ZStatInc(ZCounterPageCacheFlush, flushed);
+}
+
+class ZPageCacheFlushForUncommitClosure : public ZPageCacheFlushClosure {
+private:
+  const uint64_t _now;
+  const uint64_t _delay;
+  uint64_t       _timeout;
+
+public:
+  ZPageCacheFlushForUncommitClosure(size_t requested, uint64_t delay) :
+      ZPageCacheFlushClosure(requested),
+      _now(os::elapsedTime()),
+      _delay(delay),
+      _timeout(_delay) {}
+
+  virtual bool do_page(const ZPage* page) {
+    const uint64_t expires = page->last_used() + _delay;
+    const uint64_t timeout = expires - MIN2(expires, _now);
+
+    if (_flushed < _requested && timeout == 0) {
+      // Flush page
+      _flushed += page->size();
+      return true;
+    }
+
+    // Record shortest non-expired timeout
+    _timeout = MIN2(_timeout, timeout);
+
+    // Don't flush page
+    return false;
+  }
+
+  uint64_t timeout() const {
+    return _timeout;
+  }
+};
+
+uint64_t ZPageAllocator::uncommit(uint64_t delay) {
+  // Set the default timeout, when no pages are found in the
+  // cache or when uncommit is disabled, equal to the delay.
+  uint64_t timeout = delay;
+
+  if (!_uncommit) {
+    // Disabled
+    return timeout;
+  }
+
+  size_t capacity_before;
+  size_t capacity_after;
+  size_t uncommitted;
+
+  {
+    SuspendibleThreadSetJoiner joiner;
+    ZLocker<ZLock> locker(&_lock);
+
+    // Don't flush more than we will uncommit. Never uncommit
+    // the reserve, and never uncommit below min capacity.
+    const size_t needed = MIN2(_used + _max_reserve, _current_max_capacity);
+    const size_t guarded = MAX2(needed, _min_capacity);
+    const size_t uncommittable = _capacity - guarded;
+    const size_t uncached_available = _capacity - _used - _cache.available();
+    size_t uncommit = MIN2(uncommittable, uncached_available);
+    const size_t flush = uncommittable - uncommit;
+
+    if (flush > 0) {
+      // Flush pages to uncommit
+      ZPageCacheFlushForUncommitClosure cl(flush, delay);
+      uncommit += flush_cache(&cl);
+      timeout = cl.timeout();
+    }
+
+    // Uncommit
+    uncommitted = _physical.uncommit(uncommit);
+    _capacity -= uncommitted;
+
+    capacity_after = _capacity;
+    capacity_before = capacity_after + uncommitted;
+  }
+
+  if (uncommitted > 0) {
+    log_info(gc, heap)("Capacity: " SIZE_FORMAT "M(%.0lf%%)->" SIZE_FORMAT "M(%.0lf%%), "
+                       "Uncommitted: " SIZE_FORMAT "M",
+                       capacity_before / M, percent_of(capacity_before, max_capacity()),
+                       capacity_after / M, percent_of(capacity_after, max_capacity()),
+                       uncommitted / M);
+
+    // Update statistics
+    ZStatInc(ZCounterUncommit, uncommitted);
+  }
+
+  return timeout;
 }
 
 void ZPageAllocator::enable_deferred_delete() const {
@@ -525,6 +679,35 @@
   _safe_delete.disable_deferred_delete();
 }
 
+void ZPageAllocator::debug_map_page(const ZPage* page) const {
+  assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint");
+  _physical.debug_map(page->physical_memory(), page->start());
+}
+
+class ZPageCacheDebugMapClosure : public StackObj {
+private:
+  const ZPageAllocator* const _allocator;
+
+public:
+  ZPageCacheDebugMapClosure(const ZPageAllocator* allocator) :
+      _allocator(allocator) {}
+
+  virtual void do_page(const ZPage* page) {
+    _allocator->debug_map_page(page);
+  }
+};
+
+void ZPageAllocator::debug_map_cached_pages() const {
+  assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint");
+  ZPageCacheDebugMapClosure cl(this);
+  _cache.pages_do(&cl);
+}
+
+void ZPageAllocator::debug_unmap_all_pages() const {
+  assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint");
+  _physical.debug_unmap(ZPhysicalMemorySegment(0 /* start */, ZAddressOffsetMax), 0 /* offset */);
+}
+
 bool ZPageAllocator::is_alloc_stalled() const {
   assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint");
   return !_queue.is_empty();
--- a/src/hotspot/share/gc/z/zPageAllocator.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPageAllocator.hpp	Tue May 14 09:55:02 2019 +0200
@@ -29,7 +29,6 @@
 #include "gc/z/zLock.hpp"
 #include "gc/z/zPageCache.hpp"
 #include "gc/z/zPhysicalMemory.hpp"
-#include "gc/z/zPreMappedMemory.hpp"
 #include "gc/z/zSafeDelete.hpp"
 #include "gc/z/zVirtualMemory.hpp"
 #include "memory/allocation.hpp"
@@ -44,8 +43,11 @@
   ZVirtualMemoryManager      _virtual;
   ZPhysicalMemoryManager     _physical;
   ZPageCache                 _cache;
+  const size_t               _min_capacity;
+  const size_t               _max_capacity;
   const size_t               _max_reserve;
-  ZPreMappedMemory           _pre_mapped;
+  size_t                     _current_max_capacity;
+  size_t                     _capacity;
   size_t                     _used_high;
   size_t                     _used_low;
   size_t                     _used;
@@ -53,39 +55,44 @@
   ssize_t                    _reclaimed;
   ZList<ZPageAllocRequest>   _queue;
   mutable ZSafeDelete<ZPage> _safe_delete;
+  bool                       _uncommit;
+  bool                       _initialized;
 
   static ZPage* const      gc_marker;
 
+  void prime_cache(size_t size);
+
   void increase_used(size_t size, bool relocation);
   void decrease_used(size_t size, bool reclaimed);
 
-  size_t max_available(bool no_reserve) const;
-  size_t try_ensure_unused(size_t size, bool no_reserve);
-  size_t try_ensure_unused_for_pre_mapped(size_t size);
-
   ZPage* create_page(uint8_t type, size_t size);
   void destroy_page(ZPage* page);
 
-  void flush_pre_mapped();
-  void flush_cache(ZPageCacheFlushClosure* cl);
-  void evict_cache(size_t requested);
+  size_t max_available(bool no_reserve) const;
+  bool ensure_available(size_t size, bool no_reserve);
+  void ensure_uncached_available(size_t size);
 
   void check_out_of_memory_during_initialization();
 
-  ZPage* alloc_page_common_inner(uint8_t type, size_t size, ZAllocationFlags flags);
+  ZPage* alloc_page_common_inner(uint8_t type, size_t size, bool no_reserve);
   ZPage* alloc_page_common(uint8_t type, size_t size, ZAllocationFlags flags);
   ZPage* alloc_page_blocking(uint8_t type, size_t size, ZAllocationFlags flags);
   ZPage* alloc_page_nonblocking(uint8_t type, size_t size, ZAllocationFlags flags);
 
+  size_t flush_cache(ZPageCacheFlushClosure* cl);
+  void flush_cache_for_allocation(size_t requested);
+
   void satisfy_alloc_queue();
 
-  void detach_memory(const ZVirtualMemory& vmem, ZPhysicalMemory& pmem);
-
 public:
-  ZPageAllocator(size_t min_capacity, size_t max_capacity, size_t max_reserve);
+  ZPageAllocator(size_t min_capacity,
+                 size_t initial_capacity,
+                 size_t max_capacity,
+                 size_t max_reserve);
 
   bool is_initialized() const;
 
+  size_t min_capacity() const;
   size_t max_capacity() const;
   size_t current_max_capacity() const;
   size_t capacity() const;
@@ -102,11 +109,16 @@
   ZPage* alloc_page(uint8_t type, size_t size, ZAllocationFlags flags);
   void free_page(ZPage* page, bool reclaimed);
 
+  uint64_t uncommit(uint64_t delay);
+
   void enable_deferred_delete() const;
   void disable_deferred_delete() const;
 
-  void map_page(ZPage* page);
-  void unmap_all_pages();
+  void map_page(const ZPage* page) const;
+
+  void debug_map_page(const ZPage* page) const;
+  void debug_map_cached_pages() const;
+  void debug_unmap_all_pages() const;
 
   bool is_alloc_stalled() const;
   void check_out_of_memory();
--- a/src/hotspot/share/gc/z/zPageCache.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPageCache.cpp	Tue May 14 09:55:02 2019 +0200
@@ -31,8 +31,17 @@
 
 static const ZStatCounter ZCounterPageCacheHitL1("Memory", "Page Cache Hit L1", ZStatUnitOpsPerSecond);
 static const ZStatCounter ZCounterPageCacheHitL2("Memory", "Page Cache Hit L2", ZStatUnitOpsPerSecond);
+static const ZStatCounter ZCounterPageCacheHitL3("Memory", "Page Cache Hit L3", ZStatUnitOpsPerSecond);
 static const ZStatCounter ZCounterPageCacheMiss("Memory", "Page Cache Miss", ZStatUnitOpsPerSecond);
 
+ZPageCacheFlushClosure::ZPageCacheFlushClosure(size_t requested) :
+    _requested(requested),
+    _flushed(0) {}
+
+size_t ZPageCacheFlushClosure::overflushed() const {
+  return _flushed > _requested ? _flushed - _requested : 0;
+}
+
 ZPageCache::ZPageCache() :
     _available(0),
     _small(),
@@ -67,40 +76,73 @@
     remote_numa_id++;
   }
 
-  ZStatInc(ZCounterPageCacheMiss);
   return NULL;
 }
 
 ZPage* ZPageCache::alloc_medium_page() {
-  ZPage* const l1_page = _medium.remove_first();
-  if (l1_page != NULL) {
+  ZPage* const page = _medium.remove_first();
+  if (page != NULL) {
     ZStatInc(ZCounterPageCacheHitL1);
-    return l1_page;
+    return page;
   }
 
-  ZStatInc(ZCounterPageCacheMiss);
   return NULL;
 }
 
 ZPage* ZPageCache::alloc_large_page(size_t size) {
   // Find a page with the right size
   ZListIterator<ZPage> iter(&_large);
-  for (ZPage* l1_page; iter.next(&l1_page);) {
-    if (l1_page->size() == size) {
+  for (ZPage* page; iter.next(&page);) {
+    if (size == page->size()) {
       // Page found
-      _large.remove(l1_page);
+      _large.remove(page);
       ZStatInc(ZCounterPageCacheHitL1);
-      return l1_page;
+      return page;
     }
   }
 
-  ZStatInc(ZCounterPageCacheMiss);
+  return NULL;
+}
+
+ZPage* ZPageCache::alloc_oversized_medium_page(size_t size) {
+  if (size <= ZPageSizeMedium) {
+    return _medium.remove_first();
+  }
+
   return NULL;
 }
 
+ZPage* ZPageCache::alloc_oversized_large_page(size_t size) {
+  // Find a page that is large enough
+  ZListIterator<ZPage> iter(&_large);
+  for (ZPage* page; iter.next(&page);) {
+    if (size <= page->size()) {
+      // Page found
+      _large.remove(page);
+      return page;
+    }
+  }
+
+  return NULL;
+}
+
+ZPage* ZPageCache::alloc_oversized_page(size_t size) {
+  ZPage* page = alloc_oversized_large_page(size);
+  if (page == NULL) {
+    page = alloc_oversized_medium_page(size);
+  }
+
+  if (page != NULL) {
+    ZStatInc(ZCounterPageCacheHitL3);
+  }
+
+  return page;
+}
+
 ZPage* ZPageCache::alloc_page(uint8_t type, size_t size) {
   ZPage* page;
 
+  // Try allocate exact page
   if (type == ZPageTypeSmall) {
     page = alloc_small_page();
   } else if (type == ZPageTypeMedium) {
@@ -109,14 +151,33 @@
     page = alloc_large_page(size);
   }
 
+  if (page == NULL) {
+    // Try allocate potentially oversized page
+    ZPage* const oversized = alloc_oversized_page(size);
+    if (oversized != NULL) {
+      if (size < oversized->size()) {
+        // Split oversized page
+        page = oversized->split(type, size);
+
+        // Cache remainder
+        free_page_inner(oversized);
+      } else {
+        // Re-type correctly sized page
+        page = oversized->retype(type);
+      }
+    }
+  }
+
   if (page != NULL) {
     _available -= page->size();
+  } else {
+    ZStatInc(ZCounterPageCacheMiss);
   }
 
   return page;
 }
 
-void ZPageCache::free_page(ZPage* page) {
+void ZPageCache::free_page_inner(ZPage* page) {
   const uint8_t type = page->type();
   if (type == ZPageTypeSmall) {
     _small.get(page->numa_id()).insert_first(page);
@@ -125,7 +186,10 @@
   } else {
     _large.insert_first(page);
   }
+}
 
+void ZPageCache::free_page(ZPage* page) {
+  free_page_inner(page);
   _available += page->size();
 }
 
--- a/src/hotspot/share/gc/z/zPageCache.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPageCache.hpp	Tue May 14 09:55:02 2019 +0200
@@ -30,7 +30,13 @@
 #include "memory/allocation.hpp"
 
 class ZPageCacheFlushClosure : public StackObj {
+protected:
+  const size_t _requested;
+  size_t       _flushed;
+
 public:
+  ZPageCacheFlushClosure(size_t requested);
+  size_t overflushed() const;
   virtual bool do_page(const ZPage* page) = 0;
 };
 
@@ -45,6 +51,12 @@
   ZPage* alloc_medium_page();
   ZPage* alloc_large_page(size_t size);
 
+  ZPage* alloc_oversized_medium_page(size_t size);
+  ZPage* alloc_oversized_large_page(size_t size);
+  ZPage* alloc_oversized_page(size_t size);
+
+  void free_page_inner(ZPage* page);
+
   bool flush_list_inner(ZPageCacheFlushClosure* cl, ZList<ZPage>* from, ZList<ZPage>* to);
   void flush_list(ZPageCacheFlushClosure* cl, ZList<ZPage>* from, ZList<ZPage>* to);
   void flush_per_numa_lists(ZPageCacheFlushClosure* cl, ZPerNUMA<ZList<ZPage> >* from, ZList<ZPage>* to);
@@ -58,6 +70,8 @@
   void free_page(ZPage* page);
 
   void flush(ZPageCacheFlushClosure* cl, ZList<ZPage>* to);
+
+  template <typename Closure> void pages_do(Closure* cl) const;
 };
 
 #endif // SHARE_GC_Z_ZPAGECACHE_HPP
--- a/src/hotspot/share/gc/z/zPageCache.inline.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPageCache.inline.hpp	Tue May 14 09:55:02 2019 +0200
@@ -24,10 +24,35 @@
 #ifndef SHARE_GC_Z_ZPAGECACHE_INLINE_HPP
 #define SHARE_GC_Z_ZPAGECACHE_INLINE_HPP
 
+#include "gc/z/zList.inline.hpp"
 #include "gc/z/zPageCache.hpp"
 
 inline size_t ZPageCache::available() const {
   return _available;
 }
 
+template <typename Closure>
+inline void ZPageCache::pages_do(Closure* cl) const {
+  // Small
+  ZPerNUMAConstIterator<ZList<ZPage> > iter_numa(&_small);
+  for (const ZList<ZPage>* list; iter_numa.next(&list);) {
+    ZListIterator<ZPage> iter_small(list);
+    for (ZPage* page; iter_small.next(&page);) {
+      cl->do_page(page);
+    }
+  }
+
+  // Medium
+  ZListIterator<ZPage> iter_medium(&_medium);
+  for (ZPage* page; iter_medium.next(&page);) {
+    cl->do_page(page);
+  }
+
+  // Large
+  ZListIterator<ZPage> iter_large(&_large);
+  for (ZPage* page; iter_large.next(&page);) {
+    cl->do_page(page);
+  }
+}
+
 #endif // SHARE_GC_Z_ZPAGECACHE_INLINE_HPP
--- a/src/hotspot/share/gc/z/zPhysicalMemory.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPhysicalMemory.cpp	Tue May 14 09:55:02 2019 +0200
@@ -33,18 +33,42 @@
     _nsegments(0),
     _segments(NULL) {}
 
-ZPhysicalMemory::ZPhysicalMemory(size_t size) :
-    _nsegments(0),
-    _segments(NULL) {
-  add_segment(ZPhysicalMemorySegment(0, size));
-}
-
 ZPhysicalMemory::ZPhysicalMemory(const ZPhysicalMemorySegment& segment) :
     _nsegments(0),
     _segments(NULL) {
   add_segment(segment);
 }
 
+ZPhysicalMemory::ZPhysicalMemory(const ZPhysicalMemory& pmem) :
+    _nsegments(0),
+    _segments(NULL) {
+
+  // Copy segments
+  for (size_t i = 0; i < pmem.nsegments(); i++) {
+    add_segment(pmem.segment(i));
+  }
+}
+
+const ZPhysicalMemory& ZPhysicalMemory::operator=(const ZPhysicalMemory& pmem) {
+  // Free segments
+  delete [] _segments;
+  _segments = NULL;
+  _nsegments = 0;
+
+  // Copy segments
+  for (size_t i = 0; i < pmem.nsegments(); i++) {
+    add_segment(pmem.segment(i));
+  }
+
+  return *this;
+}
+
+ZPhysicalMemory::~ZPhysicalMemory() {
+  delete [] _segments;
+  _segments = NULL;
+  _nsegments = 0;
+}
+
 size_t ZPhysicalMemory::size() const {
   size_t size = 0;
 
@@ -55,134 +79,114 @@
   return size;
 }
 
-void ZPhysicalMemory::add_segment(ZPhysicalMemorySegment segment) {
+void ZPhysicalMemory::add_segment(const ZPhysicalMemorySegment& segment) {
   // Try merge with last segment
   if (_nsegments > 0) {
     ZPhysicalMemorySegment& last = _segments[_nsegments - 1];
     assert(last.end() <= segment.start(), "Segments added out of order");
     if (last.end() == segment.start()) {
-      // Merge
-      last.expand(segment.size());
+      last = ZPhysicalMemorySegment(last.start(), last.size() + segment.size());
       return;
     }
   }
 
-  // Make room for a new segment
-  const size_t size = sizeof(ZPhysicalMemorySegment) * (_nsegments + 1);
-  _segments = (ZPhysicalMemorySegment*)ReallocateHeap((char*)_segments, size, mtGC);
+  // Resize array
+  ZPhysicalMemorySegment* const old_segments = _segments;
+  _segments = new ZPhysicalMemorySegment[_nsegments + 1];
+  for (size_t i = 0; i < _nsegments; i++) {
+    _segments[i] = old_segments[i];
+  }
+  delete [] old_segments;
 
   // Add new segment
   _segments[_nsegments] = segment;
   _nsegments++;
 }
 
-ZPhysicalMemory ZPhysicalMemory::split(size_t split_size) {
-  // Only splitting of single-segment instances have been implemented.
-  assert(nsegments() == 1, "Can only have one segment");
-  assert(split_size <= size(), "Invalid size");
-  return ZPhysicalMemory(_segments[0].split(split_size));
-}
+ZPhysicalMemory ZPhysicalMemory::split(size_t size) {
+  ZPhysicalMemory pmem;
+  size_t nsegments = 0;
 
-void ZPhysicalMemory::clear() {
-  if (_segments != NULL) {
-    FreeHeap(_segments);
-    _segments = NULL;
-    _nsegments = 0;
+  for (size_t i = 0; i < _nsegments; i++) {
+    const ZPhysicalMemorySegment& segment = _segments[i];
+    if (pmem.size() < size) {
+      if (pmem.size() + segment.size() <= size) {
+        // Transfer segment
+        pmem.add_segment(segment);
+      } else {
+        // Split segment
+        const size_t split_size = size - pmem.size();
+        pmem.add_segment(ZPhysicalMemorySegment(segment.start(), split_size));
+        _segments[nsegments++] = ZPhysicalMemorySegment(segment.start() + split_size, segment.size() - split_size);
+      }
+    } else {
+      // Keep segment
+      _segments[nsegments++] = segment;
+    }
   }
+
+  _nsegments = nsegments;
+
+  return pmem;
 }
 
-ZPhysicalMemoryManager::ZPhysicalMemoryManager(size_t max_capacity) :
-    _backing(max_capacity),
-    _max_capacity(max_capacity),
-    _current_max_capacity(max_capacity),
-    _capacity(0),
-    _used(0) {}
-
 bool ZPhysicalMemoryManager::is_initialized() const {
   return _backing.is_initialized();
 }
 
-void ZPhysicalMemoryManager::try_ensure_unused_capacity(size_t size) {
-  const size_t unused = unused_capacity();
-  if (unused >= size) {
-    // Don't try to expand, enough unused capacity available
-    return;
-  }
-
-  const size_t current_max = current_max_capacity();
-  if (_capacity == current_max) {
-    // Don't try to expand, current max capacity reached
-    return;
-  }
-
-  // Try to expand
-  const size_t old_capacity = capacity();
-  const size_t new_capacity = MIN2(old_capacity + size - unused, current_max);
-  _capacity = _backing.try_expand(old_capacity, new_capacity);
-
-  if (_capacity != new_capacity) {
-    // Failed, or partly failed, to expand
-    log_error(gc, init)("Not enough space available on the backing filesystem to hold the current max");
-    log_error(gc, init)("Java heap size (" SIZE_FORMAT "M). Forcefully lowering max Java heap size to "
-                        SIZE_FORMAT "M (%.0lf%%).", current_max / M, _capacity / M,
-                        percent_of(_capacity, current_max));
-
-    // Adjust current max capacity to avoid further expand attempts
-    _current_max_capacity = _capacity;
-  }
+void ZPhysicalMemoryManager::warn_commit_limits(size_t max) const {
+  _backing.warn_commit_limits(max);
 }
 
-void ZPhysicalMemoryManager::nmt_commit(ZPhysicalMemory pmem, uintptr_t offset) {
+bool ZPhysicalMemoryManager::supports_uncommit() {
+  return _backing.supports_uncommit();
+}
+
+void ZPhysicalMemoryManager::nmt_commit(const ZPhysicalMemory& pmem, uintptr_t offset) const {
   const uintptr_t addr = _backing.nmt_address(offset);
   const size_t size = pmem.size();
   MemTracker::record_virtual_memory_commit((void*)addr, size, CALLER_PC);
 }
 
-void ZPhysicalMemoryManager::nmt_uncommit(ZPhysicalMemory pmem, uintptr_t offset) {
+void ZPhysicalMemoryManager::nmt_uncommit(const ZPhysicalMemory& pmem, uintptr_t offset) const {
   if (MemTracker::tracking_level() > NMT_minimal) {
     const uintptr_t addr = _backing.nmt_address(offset);
     const size_t size = pmem.size();
-
     Tracker tracker(Tracker::uncommit);
     tracker.record((address)addr, size);
   }
 }
 
+size_t ZPhysicalMemoryManager::commit(size_t size) {
+  return _backing.commit(size);
+}
+
+size_t ZPhysicalMemoryManager::uncommit(size_t size) {
+  return _backing.uncommit(size);
+}
+
 ZPhysicalMemory ZPhysicalMemoryManager::alloc(size_t size) {
-  if (unused_capacity() < size) {
-    // Not enough memory available
-    return ZPhysicalMemory();
-  }
-
-  _used += size;
   return _backing.alloc(size);
 }
 
-void ZPhysicalMemoryManager::free(ZPhysicalMemory pmem) {
+void ZPhysicalMemoryManager::free(const ZPhysicalMemory& pmem) {
   _backing.free(pmem);
-  _used -= pmem.size();
 }
 
-void ZPhysicalMemoryManager::map(ZPhysicalMemory pmem, uintptr_t offset) {
-  // Map page
+void ZPhysicalMemoryManager::map(const ZPhysicalMemory& pmem, uintptr_t offset) const {
   _backing.map(pmem, offset);
-
-  // Update native memory tracker
   nmt_commit(pmem, offset);
 }
 
-void ZPhysicalMemoryManager::unmap(ZPhysicalMemory pmem, uintptr_t offset) {
-  // Update native memory tracker
+void ZPhysicalMemoryManager::unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const {
   nmt_uncommit(pmem, offset);
-
-  // Unmap page
   _backing.unmap(pmem, offset);
 }
 
-void ZPhysicalMemoryManager::debug_map(ZPhysicalMemory pmem, uintptr_t offset) {
+void ZPhysicalMemoryManager::debug_map(const ZPhysicalMemory& pmem, uintptr_t offset) const {
   _backing.debug_map(pmem, offset);
 }
 
-void ZPhysicalMemoryManager::debug_unmap(ZPhysicalMemory pmem, uintptr_t offset) {
+void ZPhysicalMemoryManager::debug_unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const {
   _backing.debug_unmap(pmem, offset);
 }
--- a/src/hotspot/share/gc/z/zPhysicalMemory.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPhysicalMemory.hpp	Tue May 14 09:55:02 2019 +0200
@@ -27,20 +27,18 @@
 #include "memory/allocation.hpp"
 #include OS_CPU_HEADER(gc/z/zPhysicalMemoryBacking)
 
-class ZPhysicalMemorySegment {
+class ZPhysicalMemorySegment : public CHeapObj<mtGC> {
 private:
   uintptr_t _start;
   uintptr_t _end;
 
 public:
+  ZPhysicalMemorySegment();
   ZPhysicalMemorySegment(uintptr_t start, size_t size);
 
   uintptr_t start() const;
   uintptr_t end() const;
   size_t size() const;
-
-  void expand(size_t size);
-  ZPhysicalMemorySegment split(size_t size);
 };
 
 class ZPhysicalMemory {
@@ -50,53 +48,45 @@
 
 public:
   ZPhysicalMemory();
-  ZPhysicalMemory(size_t size);
   ZPhysicalMemory(const ZPhysicalMemorySegment& segment);
+  ZPhysicalMemory(const ZPhysicalMemory& pmem);
+  const ZPhysicalMemory& operator=(const ZPhysicalMemory& pmem);
+  ~ZPhysicalMemory();
 
   bool is_null() const;
   size_t size() const;
 
   size_t nsegments() const;
-  ZPhysicalMemorySegment segment(size_t index) const;
-  void add_segment(ZPhysicalMemorySegment segment);
+  const ZPhysicalMemorySegment& segment(size_t index) const;
+  void add_segment(const ZPhysicalMemorySegment& segment);
 
   ZPhysicalMemory split(size_t size);
-  void clear();
 };
 
 class ZPhysicalMemoryManager {
-  friend class VMStructs;
-
 private:
   ZPhysicalMemoryBacking _backing;
-  const size_t           _max_capacity;
-  size_t                 _current_max_capacity;
-  size_t                 _capacity;
-  size_t                 _used;
 
-  void nmt_commit(ZPhysicalMemory pmem, uintptr_t offset);
-  void nmt_uncommit(ZPhysicalMemory pmem, uintptr_t offset);
+  void nmt_commit(const ZPhysicalMemory& pmem, uintptr_t offset) const;
+  void nmt_uncommit(const ZPhysicalMemory& pmem, uintptr_t offset) const;
 
 public:
-  ZPhysicalMemoryManager(size_t max_capacity);
-
   bool is_initialized() const;
 
-  size_t max_capacity() const;
-  size_t current_max_capacity() const;
-  size_t capacity() const;
-  size_t unused_capacity() const;
+  void warn_commit_limits(size_t max) const;
+  bool supports_uncommit();
 
-  void try_ensure_unused_capacity(size_t size);
+  size_t commit(size_t size);
+  size_t uncommit(size_t size);
 
   ZPhysicalMemory alloc(size_t size);
-  void free(ZPhysicalMemory pmem);
+  void free(const ZPhysicalMemory& pmem);
 
-  void map(ZPhysicalMemory pmem, uintptr_t offset);
-  void unmap(ZPhysicalMemory pmem, uintptr_t offset);
+  void map(const ZPhysicalMemory& pmem, uintptr_t offset) const;
+  void unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const;
 
-  void debug_map(ZPhysicalMemory pmem, uintptr_t offset);
-  void debug_unmap(ZPhysicalMemory pmem, uintptr_t offset);
+  void debug_map(const ZPhysicalMemory& pmem, uintptr_t offset) const;
+  void debug_unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const;
 };
 
 #endif // SHARE_GC_Z_ZPHYSICALMEMORY_HPP
--- a/src/hotspot/share/gc/z/zPhysicalMemory.inline.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zPhysicalMemory.inline.hpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -27,6 +27,10 @@
 #include "gc/z/zPhysicalMemory.hpp"
 #include "utilities/debug.hpp"
 
+inline ZPhysicalMemorySegment::ZPhysicalMemorySegment() :
+    _start(UINTPTR_MAX),
+    _end(UINTPTR_MAX) {}
+
 inline ZPhysicalMemorySegment::ZPhysicalMemorySegment(uintptr_t start, size_t size) :
     _start(start),
     _end(start + size) {}
@@ -40,18 +44,7 @@
 }
 
 inline size_t ZPhysicalMemorySegment::size() const {
-  return end() - start();
-}
-
-inline void ZPhysicalMemorySegment::expand(size_t size) {
-  _end += size;
-}
-
-inline ZPhysicalMemorySegment ZPhysicalMemorySegment::split(size_t split_size) {
-  assert(split_size <= size(), "Invalid size");
-  ZPhysicalMemorySegment segment(_start, split_size);
-  _start += split_size;
-  return segment;
+  return _end - _start;
 }
 
 inline bool ZPhysicalMemory::is_null() const {
@@ -62,25 +55,9 @@
   return _nsegments;
 }
 
-inline ZPhysicalMemorySegment ZPhysicalMemory::segment(size_t index) const {
+inline const ZPhysicalMemorySegment& ZPhysicalMemory::segment(size_t index) const {
   assert(index < _nsegments, "Invalid segment index");
   return _segments[index];
 }
 
-inline size_t ZPhysicalMemoryManager::max_capacity() const {
-  return _max_capacity;
-}
-
-inline size_t ZPhysicalMemoryManager::current_max_capacity() const {
-  return _current_max_capacity;
-}
-
-inline size_t ZPhysicalMemoryManager::capacity() const {
-  return _capacity;
-}
-
-inline size_t ZPhysicalMemoryManager::unused_capacity() const {
-  return _capacity - _used;
-}
-
 #endif // SHARE_GC_Z_ZPHYSICALMEMORY_INLINE_HPP
--- a/src/hotspot/share/gc/z/zPreMappedMemory.cpp	Tue May 14 09:12:06 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, 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.
- */
-
-#include "precompiled.hpp"
-#include "gc/z/zPage.inline.hpp"
-#include "gc/z/zPhysicalMemory.inline.hpp"
-#include "gc/z/zPreMappedMemory.inline.hpp"
-#include "gc/z/zVirtualMemory.inline.hpp"
-#include "logging/log.hpp"
-
-ZPreMappedMemory::ZPreMappedMemory(ZVirtualMemoryManager &vmm, ZPhysicalMemoryManager &pmm, size_t size) :
-    _vmem(),
-    _pmem(),
-    _initialized(false) {
-  if (!vmm.is_initialized() || !pmm.is_initialized()) {
-    // Not initialized
-    return;
-  }
-
-  // Pre-mapping and pre-touching memory can take a long time. Log a message
-  // to help the user understand why the JVM might seem slow to start.
-  log_info(gc, init)("Pre-touching: %s", AlwaysPreTouch ? "Enabled" : "Disabled");
-  log_info(gc, init)("Pre-mapping: " SIZE_FORMAT "M", size / M);
-
-  if (size > 0) {
-    _pmem = pmm.alloc(size);
-    if (_pmem.is_null()) {
-      // Out of memory
-      log_error(gc, init)("Failed to pre-map Java heap (Cannot allocate physical memory)");
-      return;
-    }
-
-    _vmem = vmm.alloc(size, true /* alloc_from_front */);
-    if (_vmem.is_null()) {
-      // Out of address space
-      log_error(gc, init)("Failed to pre-map Java heap (Cannot allocate virtual memory)");
-      pmm.free(_pmem);
-      return;
-    }
-
-    // Map physical memory
-    pmm.map(_pmem, _vmem.start());
-  }
-
-  _initialized = true;
-}
-
-ZPage* ZPreMappedMemory::alloc_page(uint8_t type, size_t size) {
-  if (size > available()) {
-    // Not enough pre-mapped memory
-    return NULL;
-  }
-
-  // Take a chunk of the pre-mapped memory
-  const ZPhysicalMemory pmem = _pmem.split(size);
-  const ZVirtualMemory  vmem = _vmem.split(size);
-
-  ZPage* const page = new ZPage(type, vmem, pmem);
-  page->set_pre_mapped();
-
-  return page;
-}
-
-void ZPreMappedMemory::clear() {
-  assert(_pmem.is_null(), "Should be detached");
-  _vmem.clear();
-}
--- a/src/hotspot/share/gc/z/zPreMappedMemory.hpp	Tue May 14 09:12:06 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2016, 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.
- */
-
-#ifndef SHARE_GC_Z_ZPREMAPPEDMEMORY_HPP
-#define SHARE_GC_Z_ZPREMAPPEDMEMORY_HPP
-
-#include "gc/z/zPhysicalMemory.hpp"
-#include "gc/z/zVirtualMemory.hpp"
-#include "memory/allocation.hpp"
-
-class ZPage;
-
-class ZPreMappedMemory {
-private:
-  ZVirtualMemory  _vmem;
-  ZPhysicalMemory _pmem;
-  bool            _initialized;
-
-public:
-  ZPreMappedMemory(ZVirtualMemoryManager &vmm, ZPhysicalMemoryManager &pmm, size_t size);
-
-  bool is_initialized() const;
-
-  ZPhysicalMemory& physical_memory();
-  const ZVirtualMemory& virtual_memory() const;
-
-  size_t available() const;
-
-  ZPage* alloc_page(uint8_t type, size_t size);
-
-  void clear();
-};
-
-#endif // SHARE_GC_Z_ZPREMAPPEDMEMORY_HPP
--- a/src/hotspot/share/gc/z/zPreMappedMemory.inline.hpp	Tue May 14 09:12:06 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2016, 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.
- */
-
-#ifndef SHARE_GC_Z_ZPREMAPPEDMEMORY_INLINE_HPP
-#define SHARE_GC_Z_ZPREMAPPEDMEMORY_INLINE_HPP
-
-#include "gc/z/zPreMappedMemory.hpp"
-
-inline bool ZPreMappedMemory::is_initialized() const {
-  return _initialized;
-}
-
-inline ZPhysicalMemory& ZPreMappedMemory::physical_memory() {
-  return _pmem;
-}
-
-inline const ZVirtualMemory& ZPreMappedMemory::virtual_memory() const {
-  return _vmem;
-}
-
-inline size_t ZPreMappedMemory::available() const {
-  return _vmem.size();
-}
-
-#endif // SHARE_GC_Z_ZPREMAPPEDMEMORY_INLINE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/z/zUncommitter.cpp	Tue May 14 09:55:02 2019 +0200
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+#include "precompiled.hpp"
+#include "gc/z/zHeap.inline.hpp"
+#include "gc/z/zUncommitter.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "runtime/os.hpp"
+
+ZUncommitter::ZUncommitter() :
+    _monitor(Monitor::leaf, "ZUncommitter", false, Monitor::_safepoint_check_never),
+    _stop(false) {
+  set_name("ZUncommitter");
+  create_and_start();
+}
+
+bool ZUncommitter::idle(uint64_t timeout) {
+  // Idle for at least one second
+  const uint64_t expires = os::elapsedTime() + MAX2(timeout, 1ul);
+
+  for (;;) {
+    // We might wake up spuriously from wait, so always recalculate
+    // the timeout after a wakeup to see if we need to wait again.
+    const uint64_t now = os::elapsedTime();
+    const uint64_t remaining = expires - MIN2(expires, now);
+
+    MonitorLocker ml(&_monitor, Monitor::_no_safepoint_check_flag);
+    if (remaining > 0 && !_stop) {
+      ml.wait(remaining * MILLIUNITS);
+    } else {
+      return !_stop;
+    }
+  }
+}
+
+void ZUncommitter::run_service() {
+  for (;;) {
+    // Try uncommit unused memory
+    const uint64_t timeout = ZHeap::heap()->uncommit(ZUncommitDelay);
+
+    log_trace(gc, heap)("Uncommit Timeout: " UINT64_FORMAT "s", timeout);
+
+    // Idle until next attempt
+    if (!idle(timeout)) {
+      return;
+    }
+  }
+}
+
+void ZUncommitter::stop_service() {
+  MonitorLocker ml(&_monitor, Monitor::_no_safepoint_check_flag);
+  _stop = true;
+  ml.notify();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/z/zUncommitter.hpp	Tue May 14 09:55:02 2019 +0200
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+#ifndef SHARE_GC_Z_ZUNCOMMITTER_HPP
+#define SHARE_GC_Z_ZUNCOMMITTER_HPP
+
+#include "gc/shared/concurrentGCThread.hpp"
+#include "runtime/mutex.hpp"
+
+class ZUncommitter : public ConcurrentGCThread {
+private:
+  Monitor _monitor;
+  bool    _stop;
+
+  bool idle(uint64_t timeout);
+
+protected:
+  virtual void run_service();
+  virtual void stop_service();
+
+public:
+  ZUncommitter();
+};
+
+#endif // SHARE_GC_Z_ZUNCOMMITTER_HPP
--- a/src/hotspot/share/gc/z/zVirtualMemory.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zVirtualMemory.cpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -72,6 +72,6 @@
   return ZVirtualMemory(start, size);
 }
 
-void ZVirtualMemoryManager::free(ZVirtualMemory vmem) {
+void ZVirtualMemoryManager::free(const ZVirtualMemory& vmem) {
   _manager.free(vmem.start(), vmem.size());
 }
--- a/src/hotspot/share/gc/z/zVirtualMemory.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zVirtualMemory.hpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -25,7 +25,6 @@
 #define SHARE_GC_Z_ZVIRTUALMEMORY_HPP
 
 #include "gc/z/zMemory.hpp"
-#include "memory/allocation.hpp"
 
 class ZVirtualMemory {
   friend class VMStructs;
@@ -42,8 +41,8 @@
   uintptr_t start() const;
   uintptr_t end() const;
   size_t size() const;
+
   ZVirtualMemory split(size_t size);
-  void clear();
 };
 
 class ZVirtualMemoryManager {
@@ -60,7 +59,7 @@
   bool is_initialized() const;
 
   ZVirtualMemory alloc(size_t size, bool alloc_from_front = false);
-  void free(ZVirtualMemory vmem);
+  void free(const ZVirtualMemory& vmem);
 };
 
 #endif // SHARE_GC_Z_ZVIRTUALMEMORY_HPP
--- a/src/hotspot/share/gc/z/zVirtualMemory.inline.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/zVirtualMemory.inline.hpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -51,16 +51,9 @@
   return _end - _start;
 }
 
-inline ZVirtualMemory ZVirtualMemory::split(size_t split_size) {
-  assert(split_size <= size(), "precondition");
-  ZVirtualMemory mem(_start, split_size);
-  _start += split_size;
-  return mem;
-}
-
-inline void ZVirtualMemory::clear() {
-  _start = UINTPTR_MAX;
-  _end = UINTPTR_MAX;
+inline ZVirtualMemory ZVirtualMemory::split(size_t size) {
+  _start += size;
+  return ZVirtualMemory(_start - size, size);
 }
 
 #endif // SHARE_GC_Z_ZVIRTUALMEMORY_INLINE_HPP
--- a/src/hotspot/share/gc/z/z_globals.hpp	Tue May 14 09:12:06 2019 +0200
+++ b/src/hotspot/share/gc/z/z_globals.hpp	Tue May 14 09:55:02 2019 +0200
@@ -56,6 +56,13 @@
   experimental(uint, ZCollectionInterval, 0,                                \
           "Force GC at a fixed time interval (in seconds)")                 \
                                                                             \
+  experimental(bool, ZUncommit, true,                                       \
+          "Uncommit unused memory")                                         \
+                                                                            \
+  experimental(uintx, ZUncommitDelay, 5 * 60,                               \
+          "Uncommit memory if it has been unused for the specified "        \
+          "amount of time (in seconds)")                                    \
+                                                                            \
   diagnostic(uint, ZStatisticsInterval, 10,                                 \
           "Time between statistics print outs (in seconds)")                \
           range(1, (uint)-1)                                                \
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/z/ZPageAllocator.java	Tue May 14 09:12:06 2019 +0200
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/z/ZPageAllocator.java	Tue May 14 09:55:02 2019 +0200
@@ -27,8 +27,6 @@
 import sun.jvm.hotspot.debugger.Address;
 import sun.jvm.hotspot.runtime.VM;
 import sun.jvm.hotspot.runtime.VMObject;
-import sun.jvm.hotspot.runtime.VMObjectFactory;
-import sun.jvm.hotspot.types.AddressField;
 import sun.jvm.hotspot.types.CIntegerField;
 import sun.jvm.hotspot.types.Type;
 import sun.jvm.hotspot.types.TypeDataBase;
@@ -37,7 +35,8 @@
 
 public class ZPageAllocator extends VMObject {
 
-    private static long physicalFieldOffset;
+    private static CIntegerField maxCapacityField;
+    private static CIntegerField capacityField;
     private static CIntegerField usedField;
 
     static {
@@ -47,21 +46,17 @@
     static private synchronized void initialize(TypeDataBase db) {
         Type type = db.lookupType("ZPageAllocator");
 
-        physicalFieldOffset = type.getAddressField("_physical").getOffset();
+        maxCapacityField = type.getCIntegerField("_max_capacity");
+        capacityField = type.getCIntegerField("_capacity");
         usedField = type.getCIntegerField("_used");
     }
 
-    private ZPhysicalMemoryManager physical() {
-      Address physicalAddr = addr.addOffsetTo(physicalFieldOffset);
-      return (ZPhysicalMemoryManager)VMObjectFactory.newObject(ZPhysicalMemoryManager.class, physicalAddr);
-    }
-
     public long maxCapacity() {
-        return physical().maxCapacity();
+        return maxCapacityField.getValue(addr);
     }
 
     public long capacity() {
-        return physical().capacity();
+        return capacityField.getValue(addr);
     }
 
     public long used() {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/z/ZPhysicalMemoryManager.java	Tue May 14 09:12:06 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * 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.
- *
- */
-
-package sun.jvm.hotspot.gc.z;
-
-import sun.jvm.hotspot.debugger.Address;
-import sun.jvm.hotspot.runtime.VM;
-import sun.jvm.hotspot.runtime.VMObject;
-import sun.jvm.hotspot.types.CIntegerField;
-import sun.jvm.hotspot.types.Type;
-import sun.jvm.hotspot.types.TypeDataBase;
-
-// Mirror class for ZPhysicalMemoryManager
-
-public class ZPhysicalMemoryManager extends VMObject {
-
-    private static CIntegerField capacityField;
-
-    private static CIntegerField maxCapacityField;
-
-    static {
-        VM.registerVMInitializedObserver((o, d) -> initialize(VM.getVM().getTypeDataBase()));
-    }
-
-    private static synchronized void initialize(TypeDataBase db) {
-        Type type = db.lookupType("ZPhysicalMemoryManager");
-
-        capacityField = type.getCIntegerField("_capacity");
-        maxCapacityField = type.getCIntegerField("_max_capacity");
-    }
-
-    public long capacity() {
-        return capacityField.getValue(addr);
-    }
-
-    public long maxCapacity() {
-        return maxCapacityField.getValue(addr);
-    }
-
-    public ZPhysicalMemoryManager(Address addr) {
-        super(addr);
-    }
-}
--- a/test/hotspot/gtest/gc/z/test_zForwarding.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/test/hotspot/gtest/gc/z/test_zForwarding.cpp	Tue May 14 09:55:02 2019 +0200
@@ -169,9 +169,6 @@
 
     // Teardown forwarding
     ZForwarding::destroy(forwarding);
-
-    // Teardown page
-    page.physical_memory().clear();
   }
 
   // Run the given function with a few different input values.
--- a/test/hotspot/gtest/gc/z/test_zPhysicalMemory.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/test/hotspot/gtest/gc/z/test_zPhysicalMemory.cpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -22,59 +22,121 @@
  */
 
 #include "precompiled.hpp"
-#include "gc/z/zGlobals.hpp"
 #include "gc/z/zPhysicalMemory.inline.hpp"
-#include "utilities/debug.hpp"
 #include "unittest.hpp"
 
-#if defined(AMD64)
+TEST(ZPhysicalMemoryTest, copy) {
+  const ZPhysicalMemorySegment seg0(0, 100);
+  const ZPhysicalMemorySegment seg1(200, 100);
+
+  ZPhysicalMemory pmem0;
+  pmem0.add_segment(seg0);
+  EXPECT_EQ(pmem0.nsegments(), 1u);
+  EXPECT_EQ(pmem0.segment(0).size(), 100u);
+
+  ZPhysicalMemory pmem1;
+  pmem1.add_segment(seg0);
+  pmem1.add_segment(seg1);
+  EXPECT_EQ(pmem1.nsegments(), 2u);
+  EXPECT_EQ(pmem1.segment(0).size(), 100u);
+  EXPECT_EQ(pmem1.segment(1).size(), 100u);
 
-TEST(ZPhysicalMemorySegmentTest, split) {
-  ZPhysicalMemorySegment seg(0, 10 * ZGranuleSize);
+  ZPhysicalMemory pmem2(pmem0);
+  EXPECT_EQ(pmem2.nsegments(), 1u);
+  EXPECT_EQ(pmem2.segment(0).size(), 100u);
+
+  pmem2 = pmem1;
+  EXPECT_EQ(pmem2.nsegments(), 2u);
+  EXPECT_EQ(pmem2.segment(0).size(), 100u);
+  EXPECT_EQ(pmem2.segment(1).size(), 100u);
+}
 
-  ZPhysicalMemorySegment seg_split0 = seg.split(0 * ZGranuleSize);
-  EXPECT_EQ(seg_split0.size(),  0 * ZGranuleSize);
-  EXPECT_EQ(       seg.size(), 10 * ZGranuleSize);
+TEST(ZPhysicalMemoryTest, segments) {
+  const ZPhysicalMemorySegment seg0(0, 1);
+  const ZPhysicalMemorySegment seg1(1, 1);
+  const ZPhysicalMemorySegment seg2(2, 1);
+  const ZPhysicalMemorySegment seg3(3, 1);
+  const ZPhysicalMemorySegment seg4(4, 1);
+  const ZPhysicalMemorySegment seg5(5, 1);
+  const ZPhysicalMemorySegment seg6(6, 1);
+
+  ZPhysicalMemory pmem0;
+  EXPECT_EQ(pmem0.nsegments(), 0u);
+  EXPECT_EQ(pmem0.is_null(), true);
 
-  ZPhysicalMemorySegment seg_split1 = seg.split(5 * ZGranuleSize);
-  EXPECT_EQ(seg_split1.size(),  5 * ZGranuleSize);
-  EXPECT_EQ(       seg.size(),  5 * ZGranuleSize);
+  ZPhysicalMemory pmem1;
+  pmem1.add_segment(seg0);
+  pmem1.add_segment(seg1);
+  pmem1.add_segment(seg2);
+  pmem1.add_segment(seg3);
+  pmem1.add_segment(seg4);
+  pmem1.add_segment(seg5);
+  pmem1.add_segment(seg6);
+  EXPECT_EQ(pmem1.nsegments(), 1u);
+  EXPECT_EQ(pmem1.segment(0).size(), 7u);
+  EXPECT_EQ(pmem1.is_null(), false);
+
+  ZPhysicalMemory pmem2;
+  pmem2.add_segment(seg0);
+  pmem2.add_segment(seg1);
+  pmem2.add_segment(seg2);
+  pmem2.add_segment(seg4);
+  pmem2.add_segment(seg5);
+  pmem2.add_segment(seg6);
+  EXPECT_EQ(pmem2.nsegments(), 2u);
+  EXPECT_EQ(pmem2.segment(0).size(), 3u);
+  EXPECT_EQ(pmem2.segment(1).size(), 3u);
+  EXPECT_EQ(pmem2.is_null(), false);
 
-  ZPhysicalMemorySegment seg_split2 = seg.split(5 * ZGranuleSize);
-  EXPECT_EQ(seg_split2.size(),  5 * ZGranuleSize);
-  EXPECT_EQ(       seg.size(),  0 * ZGranuleSize);
+  ZPhysicalMemory pmem3;
+  pmem3.add_segment(seg0);
+  pmem3.add_segment(seg2);
+  pmem3.add_segment(seg3);
+  pmem3.add_segment(seg4);
+  pmem3.add_segment(seg6);
+  EXPECT_EQ(pmem3.nsegments(), 3u);
+  EXPECT_EQ(pmem3.segment(0).size(), 1u);
+  EXPECT_EQ(pmem3.segment(1).size(), 3u);
+  EXPECT_EQ(pmem3.segment(2).size(), 1u);
+  EXPECT_EQ(pmem3.is_null(), false);
 
-  ZPhysicalMemorySegment seg_split3 = seg.split(0 * ZGranuleSize);
-  EXPECT_EQ(seg_split3.size(),  0 * ZGranuleSize);
-  EXPECT_EQ(       seg.size(),  0 * ZGranuleSize);
+  ZPhysicalMemory pmem4;
+  pmem4.add_segment(seg0);
+  pmem4.add_segment(seg2);
+  pmem4.add_segment(seg4);
+  pmem4.add_segment(seg6);
+  EXPECT_EQ(pmem4.nsegments(), 4u);
+  EXPECT_EQ(pmem4.segment(0).size(), 1u);
+  EXPECT_EQ(pmem4.segment(1).size(), 1u);
+  EXPECT_EQ(pmem4.segment(2).size(), 1u);
+  EXPECT_EQ(pmem4.segment(3).size(), 1u);
+  EXPECT_EQ(pmem4.is_null(), false);
 }
 
 TEST(ZPhysicalMemoryTest, split) {
-  ZPhysicalMemoryManager pmem_manager(10 * ZGranuleSize);
-
-  pmem_manager.try_ensure_unused_capacity(10 * ZGranuleSize);
-  EXPECT_EQ(pmem_manager.unused_capacity(), 10 * ZGranuleSize);
+  ZPhysicalMemory pmem;
 
-  ZPhysicalMemory pmem = pmem_manager.alloc(8 * ZGranuleSize);
-  EXPECT_EQ(pmem.nsegments(), 1u) << "wrong number of segments";
-
-  ZPhysicalMemory split0_pmem = pmem.split(ZGranuleSize);
-  EXPECT_EQ(split0_pmem.nsegments(), 1u);
-  EXPECT_EQ(       pmem.nsegments(), 1u);
-  EXPECT_EQ(split0_pmem.size(), 1 * ZGranuleSize);
-  EXPECT_EQ(       pmem.size(), 7 * ZGranuleSize);
+  pmem.add_segment(ZPhysicalMemorySegment(0, 10));
+  pmem.add_segment(ZPhysicalMemorySegment(10, 10));
+  pmem.add_segment(ZPhysicalMemorySegment(30, 10));
+  EXPECT_EQ(pmem.nsegments(), 2u);
+  EXPECT_EQ(pmem.size(), 30u);
 
-  ZPhysicalMemory split1_pmem = pmem.split(2 * ZGranuleSize);
-  EXPECT_EQ(split1_pmem.nsegments(), 1u);
-  EXPECT_EQ(       pmem.nsegments(), 1u);
-  EXPECT_EQ(split1_pmem.size(), 2 * ZGranuleSize);
-  EXPECT_EQ(       pmem.size(), 5 * ZGranuleSize);
+  ZPhysicalMemory pmem0 = pmem.split(1);
+  EXPECT_EQ(pmem0.nsegments(), 1u);
+  EXPECT_EQ(pmem0.size(), 1u);
+  EXPECT_EQ(pmem.nsegments(), 2u);
+  EXPECT_EQ(pmem.size(), 29u);
 
-  ZPhysicalMemory split2_pmem = pmem.split(5 * ZGranuleSize);
-  EXPECT_EQ(split2_pmem.nsegments(), 1u);
-  EXPECT_EQ(       pmem.nsegments(), 1u);
-  EXPECT_EQ(split2_pmem.size(), 5 * ZGranuleSize);
-  EXPECT_EQ(       pmem.size(), 0 * ZGranuleSize);
+  ZPhysicalMemory pmem1 = pmem.split(25);
+  EXPECT_EQ(pmem1.nsegments(), 2u);
+  EXPECT_EQ(pmem1.size(), 25u);
+  EXPECT_EQ(pmem.nsegments(), 1u);
+  EXPECT_EQ(pmem.size(), 4u);
+
+  ZPhysicalMemory pmem2 = pmem.split(4);
+  EXPECT_EQ(pmem2.nsegments(), 1u);
+  EXPECT_EQ(pmem2.size(), 4u);
+  EXPECT_EQ(pmem.nsegments(), 0u);
+  EXPECT_EQ(pmem.size(), 0u);
 }
-
-#endif
--- a/test/hotspot/gtest/gc/z/test_zVirtualMemory.cpp	Tue May 14 09:12:06 2019 +0200
+++ b/test/hotspot/gtest/gc/z/test_zVirtualMemory.cpp	Tue May 14 09:55:02 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -23,26 +23,23 @@
 
 #include "precompiled.hpp"
 #include "gc/z/zVirtualMemory.inline.hpp"
-#include "utilities/debug.hpp"
 #include "unittest.hpp"
 
 TEST(ZVirtualMemory, split) {
-  const size_t PageSize = 2 * M;
-
-  ZVirtualMemory mem(0, 10 * PageSize);
+  ZVirtualMemory vmem(0, 10);
 
-  ZVirtualMemory mem_split0 = mem.split(0 * PageSize);
-  EXPECT_EQ(mem_split0.size(),  0 * PageSize);
-  EXPECT_EQ(       mem.size(), 10 * PageSize);
+  ZVirtualMemory vmem0 = vmem.split(0);
+  EXPECT_EQ(vmem0.size(), 0u);
+  EXPECT_EQ(vmem.size(), 10u);
 
-  ZVirtualMemory mem_split1 = mem.split(5u * PageSize);
-  EXPECT_EQ(mem_split1.size(),  5 * PageSize);
-  EXPECT_EQ(       mem.size(),  5 * PageSize);
+  ZVirtualMemory vmem1 = vmem.split(5);
+  EXPECT_EQ(vmem1.size(), 5u);
+  EXPECT_EQ(vmem.size(), 5u);
 
-  ZVirtualMemory mem_split2 = mem.split(5u * PageSize);
-  EXPECT_EQ(mem_split2.size(),  5 * PageSize);
-  EXPECT_EQ(       mem.size(),  0 * PageSize);
+  ZVirtualMemory vmem2 = vmem.split(5);
+  EXPECT_EQ(vmem2.size(), 5u);
+  EXPECT_EQ(vmem.size(), 0u);
 
-  ZVirtualMemory mem_split3 = mem.split(0 * PageSize);
-  EXPECT_EQ(mem_split3.size(),  0 * PageSize);
+  ZVirtualMemory vmem3 = vmem.split(0);
+  EXPECT_EQ(vmem3.size(), 0u);
 }
--- a/test/hotspot/jtreg/ProblemList-zgc.txt	Tue May 14 09:12:06 2019 +0200
+++ b/test/hotspot/jtreg/ProblemList-zgc.txt	Tue May 14 09:55:02 2019 +0200
@@ -37,6 +37,5 @@
 serviceability/sa/TestClhsdbJstackLock.java                   8220624   generic-all
 serviceability/sa/TestHeapDumpForInvokeDynamic.java           8220624   generic-all
 serviceability/sa/TestHeapDumpForLargeArray.java              8220624   generic-all
-serviceability/sa/TestUniverse.java                           8220624   generic-all
 serviceability/sa/TestJmapCore.java                           8220624   generic-all
 serviceability/sa/TestJmapCoreMetaspace.java                  8219443   generic-all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/z/TestUncommit.java	Tue May 14 09:55:02 2019 +0200
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+package gc.z;
+
+/*
+ * @test TestUncommit
+ * @requires vm.gc.Z
+ * @summary Test ZGC uncommit unused memory
+ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xms128M -Xmx512M -XX:ZUncommitDelay=10 gc.z.TestUncommit true 3
+ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xms512M -Xmx512M -XX:ZUncommitDelay=10 gc.z.TestUncommit false 1
+ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xms128M -Xmx512M -XX:ZUncommitDelay=10 -XX:-ZUncommit gc.z.TestUncommit false 1
+ */
+
+import java.util.ArrayList;
+
+public class TestUncommit {
+    private static final int delay = 10; // seconds
+    private static final int allocSize = 200 * 1024 * 1024; // 200M
+    private static final int smallObjectSize = 4 * 1024; // 4K
+    private static final int mediumObjectSize = 2 * 1024 * 1024; // 2M
+    private static final int largeObjectSize = allocSize;
+
+    private static volatile ArrayList<byte[]> keepAlive;
+
+    private static long capacity() {
+        return Runtime.getRuntime().totalMemory();
+    }
+
+    private static void allocate(int objectSize) {
+        keepAlive = new ArrayList<>();
+        for (int i = 0; i < allocSize; i+= objectSize) {
+            keepAlive.add(new byte[objectSize]);
+        }
+    }
+
+    private static void reclaim() {
+        keepAlive = null;
+        System.gc();
+    }
+
+    private static void test(boolean enabled, int objectSize) throws Exception {
+        final var beforeAlloc = capacity();
+
+        // Allocate memory
+        allocate(objectSize);
+
+        final var afterAlloc = capacity();
+
+        // Reclaim memory
+        reclaim();
+
+        // Wait shorter than the uncommit delay
+        Thread.sleep(delay * 1000 / 2);
+
+        final var beforeUncommit = capacity();
+
+        // Wait longer than the uncommit delay
+        Thread.sleep(delay * 1000);
+
+        final var afterUncommit = capacity();
+
+        System.out.println("  Uncommit Enabled: " + enabled);
+        System.out.println("    Uncommit Delay: " + delay);
+        System.out.println("       Object Size: " + objectSize);
+        System.out.println("        Alloc Size: " + allocSize);
+        System.out.println("      Before Alloc: " + beforeAlloc);
+        System.out.println("       After Alloc: " + afterAlloc);
+        System.out.println("   Before Uncommit: " + beforeUncommit);
+        System.out.println("    After Uncommit: " + afterUncommit);
+        System.out.println();
+
+        // Verify
+        if (enabled) {
+            if (beforeUncommit == beforeAlloc) {
+                throw new Exception("Uncommitted too fast");
+            }
+
+            if (afterUncommit >= afterAlloc) {
+                throw new Exception("Uncommitted too slow");
+            }
+
+            if (afterUncommit < beforeAlloc) {
+                throw new Exception("Uncommitted too much");
+            }
+
+            if (afterUncommit > beforeAlloc) {
+                throw new Exception("Uncommitted too little");
+            }
+        } else {
+            if (afterAlloc > beforeUncommit ||
+                afterAlloc > afterUncommit) {
+                throw new Exception("Should not uncommit");
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        final boolean enabled = Boolean.parseBoolean(args[0]);
+        final int iterations = Integer.parseInt(args[1]);
+
+        for (int i = 0; i < iterations; i++) {
+            System.out.println("Iteration " + i);
+            test(enabled, smallObjectSize);
+            test(enabled, mediumObjectSize);
+            test(enabled, largeObjectSize);
+        }
+    }
+}