8205924: ZGC: Premature OOME due to failure to expand backing file
authorpliden
Wed, 04 Jul 2018 12:04:02 +0200
changeset 50979 270b11dadbaf
parent 50978 a63f6915a1f9
child 50980 343d3c0dd368
8205924: ZGC: Premature OOME due to failure to expand backing file Reviewed-by: ehelin
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/zPhysicalMemoryBacking_linux_x86.cpp
src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.hpp
src/hotspot/share/gc/z/zDirector.cpp
src/hotspot/share/gc/z/zHeap.cpp
src/hotspot/share/gc/z/zHeap.hpp
src/hotspot/share/gc/z/zPageAllocator.cpp
src/hotspot/share/gc/z/zPageAllocator.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
test/hotspot/gtest/gc/z/test_zPhysicalMemory.cpp
--- a/src/hotspot/os_cpu/linux_x86/gc/z/zBackingFile_linux_x86.cpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/os_cpu/linux_x86/gc/z/zBackingFile_linux_x86.cpp	Wed Jul 04 12:04:02 2018 +0200
@@ -28,7 +28,6 @@
 #include "gc/z/zErrno.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"
@@ -83,9 +82,12 @@
   return syscall(__NR_memfd_create, name, flags);
 }
 
+bool ZBackingFile::_hugetlbfs_mmap_retry = true;
+
 ZBackingFile::ZBackingFile() :
     _fd(-1),
     _filesystem(0),
+    _available(0),
     _initialized(false) {
 
   // Create backing file
@@ -94,39 +96,47 @@
     return;
   }
 
-  // Get filesystem type
+  // Get filesystem statistics
   struct statfs statfs_buf;
   if (fstatfs(_fd, &statfs_buf) == -1) {
     ZErrno err;
-    log_error(gc, init)("Failed to determine filesystem type for backing file (%s)", err.to_string());
+    log_error(gc, init)("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;
 
   // 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, init)("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, init)("-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, init)("-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, init)("-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, init)("-XX:+UseLargePages must be enabled when using a %s filesystem",
+                        ZFILESYSTEM_HUGETLBFS);
     return;
   }
 
@@ -149,7 +159,7 @@
     return -1;
   }
 
-  log_debug(gc, init)("Heap backed by file /memfd:%s", filename);
+  log_info(gc, init)("Heap backed by file: /memfd:%s", filename);
 
   return fd;
 }
@@ -181,7 +191,7 @@
       return -1;
     }
 
-    log_debug(gc, init)("Heap backed by file %s/#" UINT64_FORMAT, path.get(), (uint64_t)stat_buf.st_ino);
+    log_info(gc, init)("Heap backed by file: %s/#" UINT64_FORMAT, path.get(), (uint64_t)stat_buf.st_ino);
 
     return fd_anon;
   }
@@ -207,7 +217,7 @@
     return -1;
   }
 
-  log_debug(gc, init)("Heap backed by file %s", filename);
+  log_info(gc, init)("Heap backed by file: %s", filename);
 
   return fd;
 }
@@ -238,6 +248,10 @@
   return _fd;
 }
 
+size_t ZBackingFile::available() const {
+  return _available;
+}
+
 bool ZBackingFile::is_tmpfs() const {
   return _filesystem == TMPFS_MAGIC;
 }
@@ -292,12 +306,12 @@
   return true;
 }
 
-bool ZBackingFile::expand_tmpfs(size_t offset, size_t length) const {
+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());
 }
 
-bool ZBackingFile::expand_hugetlbfs(size_t offset, size_t length) const {
+bool ZBackingFile::try_expand_hugetlbfs(size_t offset, size_t length) const {
   assert(is_hugetlbfs(), "Wrong filesystem");
 
   // Prior to kernel 4.3, hugetlbfs did not support posix_fallocate().
@@ -320,11 +334,11 @@
   // process being returned to the huge page pool and made available for new
   // allocations.
   void* addr = MAP_FAILED;
-  const int max_attempts = 3;
+  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 || is_init_completed()) {
-      // Mapping was successful or initialization phase has completed
+    if (addr != MAP_FAILED || !_hugetlbfs_mmap_retry) {
+      // Mapping was successful or mmap retry is disabled
       break;
     }
 
@@ -337,6 +351,11 @@
     sleep(1);
   }
 
+  // Disable mmap retry from now on
+  if (_hugetlbfs_mmap_retry) {
+    _hugetlbfs_mmap_retry = false;
+  }
+
   if (addr == MAP_FAILED) {
     // Not enough huge pages left
     ZErrno err;
@@ -355,6 +374,39 @@
   return true;
 }
 
-bool ZBackingFile::expand(size_t offset, size_t length) const {
-  return is_hugetlbfs() ? expand_hugetlbfs(offset, length) : expand_tmpfs(offset, length);
+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");
+
+  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 {
+  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;
+    }
+
+    if (try_expand_tmpfs_or_hugetlbfs(start, length, alignment)) {
+      // Success, try expand more
+      start += length;
+    } else {
+      // Failed, try expand less
+      end -= length;
+    }
+  }
+}
--- a/src/hotspot/os_cpu/linux_x86/gc/z/zBackingFile_linux_x86.hpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/os_cpu/linux_x86/gc/z/zBackingFile_linux_x86.hpp	Wed Jul 04 12:04:02 2018 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -28,8 +28,11 @@
 
 class ZBackingFile {
 private:
+  static bool _hugetlbfs_mmap_retry;
+
   int      _fd;
   uint64_t _filesystem;
+  size_t   _available;
   bool     _initialized;
 
   int create_mem_fd(const char* name) const;
@@ -42,9 +45,9 @@
 
   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 expand_tmpfs(size_t offset, size_t length) const;
-
-  bool expand_hugetlbfs(size_t offset, size_t length) 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;
 
 public:
   ZBackingFile();
@@ -52,7 +55,9 @@
   bool is_initialized() const;
 
   int fd() const;
-  bool expand(size_t offset, size_t length) const;
+  size_t available() const;
+
+  size_t try_expand(size_t offset, size_t length, size_t alignment) const;
 };
 
 #endif // OS_CPU_LINUX_X86_ZBACKINGFILE_LINUX_X86_HPP
--- a/src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.cpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.cpp	Wed Jul 04 12:04:02 2018 +0200
@@ -52,8 +52,15 @@
     _file(),
     _granule_size(granule_size) {
 
-  // Check and warn if max map count seems too low
+  if (!_file.is_initialized()) {
+    return;
+  }
+
+  // Check and warn if max map count is too low
   check_max_map_count(max_capacity, granule_size);
+
+  // Check and warn if available space on filesystem is too low
+  check_available_space_on_filesystem(max_capacity);
 }
 
 void ZPhysicalMemoryBacking::check_max_map_count(size_t max_capacity, size_t granule_size) const {
@@ -61,7 +68,7 @@
   FILE* const file = fopen(filename, "r");
   if (file == NULL) {
     // Failed to open file, skip check
-    log_debug(gc)("Failed to open %s", filename);
+    log_debug(gc, init)("Failed to open %s", filename);
     return;
   }
 
@@ -70,7 +77,7 @@
   fclose(file);
   if (result != 1) {
     // Failed to read file, skip check
-    log_debug(gc)("Failed to read %s", filename);
+    log_debug(gc, init)("Failed to read %s", filename);
     return;
   }
 
@@ -81,15 +88,43 @@
   // 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 / granule_size) * 3 * 1.2;
   if (actual_max_map_count < required_max_map_count) {
-    log_warning(gc)("The system limit on number of memory mappings "
-                    "per process might be too low for the given");
-    log_warning(gc)("Java heap size (" SIZE_FORMAT "M). Please "
-                    "adjust %s to allow for at least", max_capacity / M, filename);
-    log_warning(gc)(SIZE_FORMAT " mappings (current limit is " SIZE_FORMAT "). "
-                    "Continuing execution with the current limit could",
-                    required_max_map_count, actual_max_map_count);
-    log_warning(gc)("lead to a fatal error down the line, due to failed "
-                    "attempts to map memory.");
+    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.");
+  }
+}
+
+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;
+  }
+
+  log_info(gc, init)("Available space on backing filesystem: " SIZE_FORMAT "M",
+                     available / M);
+
+  // 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.");
   }
 }
 
@@ -97,18 +132,16 @@
   return _file.is_initialized();
 }
 
-bool ZPhysicalMemoryBacking::expand(size_t from, size_t to) {
-  const size_t size = to - from;
+size_t ZPhysicalMemoryBacking::try_expand(size_t old_capacity, size_t new_capacity) {
+  assert(old_capacity < new_capacity, "Invalid old/new capacity");
 
-  // Expand
-  if (!_file.expand(from, size)) {
-    return false;
+  const size_t capacity = _file.try_expand(old_capacity, new_capacity - old_capacity, _granule_size);
+  if (capacity > old_capacity) {
+    // Add expanded capacity to free list
+    _manager.free(old_capacity, capacity - old_capacity);
   }
 
-  // Add expanded space to free list
-  _manager.free(from, size);
-
-  return true;
+  return capacity;
 }
 
 ZPhysicalMemory ZPhysicalMemoryBacking::alloc(size_t size) {
--- a/src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.hpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/os_cpu/linux_x86/gc/z/zPhysicalMemoryBacking_linux_x86.hpp	Wed Jul 04 12:04:02 2018 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -37,6 +37,7 @@
   const size_t   _granule_size;
 
   void check_max_map_count(size_t max_capacity, size_t granule_size) const;
+  void check_available_space_on_filesystem(size_t max_capacity) const;
   void map_failed(ZErrno err) const;
 
   void advise_view(uintptr_t addr, size_t size) const;
@@ -49,7 +50,8 @@
 
   bool is_initialized() const;
 
-  bool expand(size_t from, size_t to);
+  size_t try_expand(size_t old_capacity, size_t new_capacity);
+
   ZPhysicalMemory alloc(size_t size);
   void free(ZPhysicalMemory pmem);
 
--- a/src/hotspot/share/gc/z/zDirector.cpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/share/gc/z/zDirector.cpp	Wed Jul 04 12:04:02 2018 +0200
@@ -81,7 +81,7 @@
   // Perform GC if heap usage passes 10/20/30% and no other GC has been
   // performed yet. This allows us to get some early samples of the GC
   // duration, which is needed by the other rules.
-  const size_t max_capacity = ZHeap::heap()->max_capacity();
+  const size_t max_capacity = ZHeap::heap()->current_max_capacity();
   const size_t used = ZHeap::heap()->used();
   const double used_threshold_percent = (ZStatCycle::ncycles() + 1) * 0.1;
   const size_t used_threshold = max_capacity * used_threshold_percent;
@@ -107,7 +107,7 @@
   // Calculate amount of free memory available to Java threads. Note that
   // the heap reserve is not available to Java threads and is therefore not
   // considered part of the free memory.
-  const size_t max_capacity = ZHeap::heap()->max_capacity();
+  const size_t max_capacity = ZHeap::heap()->current_max_capacity();
   const size_t max_reserve = ZHeap::heap()->max_reserve();
   const size_t used = ZHeap::heap()->used();
   const size_t free_with_reserve = max_capacity - used;
@@ -155,7 +155,7 @@
   // passed since the previous GC. This helps avoid superfluous GCs when running
   // applications with very low allocation rate.
   const size_t used_after_last_gc = ZStatHeap::used_at_relocate_end();
-  const size_t used_increase_threshold = ZHeap::heap()->max_capacity() * 0.10; // 10%
+  const size_t used_increase_threshold = ZHeap::heap()->current_max_capacity() * 0.10; // 10%
   const size_t used_threshold = used_after_last_gc + used_increase_threshold;
   const size_t used = ZHeap::heap()->used();
   const double time_since_last_gc = ZStatCycle::time_since_last();
--- a/src/hotspot/share/gc/z/zHeap.cpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/share/gc/z/zHeap.cpp	Wed Jul 04 12:04:02 2018 +0200
@@ -107,6 +107,10 @@
   return _page_allocator.max_capacity();
 }
 
+size_t ZHeap::current_max_capacity() const {
+  return _page_allocator.current_max_capacity();
+}
+
 size_t ZHeap::capacity() const {
   return _page_allocator.capacity();
 }
--- a/src/hotspot/share/gc/z/zHeap.hpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/share/gc/z/zHeap.hpp	Wed Jul 04 12:04:02 2018 +0200
@@ -79,6 +79,7 @@
   // Heap metrics
   size_t min_capacity() const;
   size_t max_capacity() const;
+  size_t current_max_capacity() const;
   size_t capacity() const;
   size_t max_reserve() const;
   size_t used_high() const;
--- a/src/hotspot/share/gc/z/zPageAllocator.cpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/share/gc/z/zPageAllocator.cpp	Wed Jul 04 12:04:02 2018 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -84,11 +84,12 @@
 ZPage* const ZPageAllocator::gc_marker = (ZPage*)-1;
 
 ZPageAllocator::ZPageAllocator(size_t min_capacity, size_t max_capacity, size_t max_reserve) :
+    _lock(),
     _virtual(),
     _physical(max_capacity, ZPageSizeMin),
     _cache(),
-    _pre_mapped(_virtual, _physical, min_capacity),
     _max_reserve(max_reserve),
+    _pre_mapped(_virtual, _physical, try_ensure_unused_for_pre_mapped(min_capacity)),
     _used_high(0),
     _used_low(0),
     _used(0),
@@ -107,6 +108,10 @@
   return _physical.max_capacity();
 }
 
+size_t ZPageAllocator::current_max_capacity() const {
+  return _physical.current_max_capacity();
+}
+
 size_t ZPageAllocator::capacity() const {
   return _physical.capacity();
 }
@@ -169,18 +174,43 @@
   }
 }
 
-size_t ZPageAllocator::available(ZAllocationFlags flags) const {
-  size_t available = max_capacity() - used();
-  assert(_physical.available() + _pre_mapped.available() + _cache.available()  == available, "Should be equal");
+size_t ZPageAllocator::max_available(bool no_reserve) const {
+  size_t available = current_max_capacity() - used();
 
-  if (flags.no_reserve()) {
-    // The memory reserve should not be considered free
+  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);
@@ -259,8 +289,8 @@
 }
 
 ZPage* ZPageAllocator::alloc_page_common_inner(uint8_t type, size_t size, ZAllocationFlags flags) {
-  const size_t available_total = available(flags);
-  if (available_total < size) {
+  const size_t max = max_available(flags.no_reserve());
+  if (max < size) {
     // Not enough free memory
     return NULL;
   }
@@ -281,11 +311,11 @@
   // subsequent allocations can use the physical memory.
   flush_pre_mapped();
 
-  // Check if physical memory is available
-  const size_t available_physical = _physical.available();
-  if (available_physical < size) {
+  // Try ensure that physical memory is available
+  const size_t unused = try_ensure_unused(size, flags.no_reserve());
+  if (unused < size) {
     // Flush cache to free up more physical memory
-    flush_cache(size - available_physical);
+    flush_cache(size - unused);
   }
 
   // Create new page and allocate physical memory
@@ -303,7 +333,7 @@
   increase_used(size, flags.relocation());
 
   // Send trace event
-  ZTracer::tracer()->report_page_alloc(size, used(), available(flags), _cache.available(), flags);
+  ZTracer::tracer()->report_page_alloc(size, used(), max_available(flags.no_reserve()), _cache.available(), flags);
 
   return page;
 }
--- a/src/hotspot/share/gc/z/zPageAllocator.hpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/share/gc/z/zPageAllocator.hpp	Wed Jul 04 12:04:02 2018 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -43,8 +43,8 @@
   ZVirtualMemoryManager    _virtual;
   ZPhysicalMemoryManager   _physical;
   ZPageCache               _cache;
+  const size_t             _max_reserve;
   ZPreMappedMemory         _pre_mapped;
-  const size_t             _max_reserve;
   size_t                   _used_high;
   size_t                   _used_low;
   size_t                   _used;
@@ -58,7 +58,9 @@
   void increase_used(size_t size, bool relocation);
   void decrease_used(size_t size, bool reclaimed);
 
-  size_t available(ZAllocationFlags flags) const;
+  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 map_page(ZPage* page);
@@ -83,6 +85,7 @@
   bool is_initialized() const;
 
   size_t max_capacity() const;
+  size_t current_max_capacity() const;
   size_t capacity() const;
   size_t max_reserve() const;
   size_t used_high() const;
--- a/src/hotspot/share/gc/z/zPhysicalMemory.cpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/share/gc/z/zPhysicalMemory.cpp	Wed Jul 04 12:04:02 2018 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -27,6 +27,7 @@
 #include "memory/allocation.inline.hpp"
 #include "services/memTracker.hpp"
 #include "utilities/debug.hpp"
+#include "utilities/globalDefinitions.hpp"
 
 ZPhysicalMemory::ZPhysicalMemory() :
     _nsegments(0),
@@ -93,6 +94,7 @@
 ZPhysicalMemoryManager::ZPhysicalMemoryManager(size_t max_capacity, size_t granule_size) :
     _backing(max_capacity, granule_size),
     _max_capacity(max_capacity),
+    _current_max_capacity(max_capacity),
     _capacity(0),
     _used(0) {}
 
@@ -100,31 +102,34 @@
   return _backing.is_initialized();
 }
 
-bool ZPhysicalMemoryManager::ensure_available(size_t size) {
-  const size_t unused_capacity = _capacity - _used;
-  if (unused_capacity >= size) {
-    // Enough unused capacity available
-    return true;
+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;
   }
 
-  const size_t expand_with = size - unused_capacity;
-  const size_t new_capacity = _capacity + expand_with;
-  if (new_capacity > _max_capacity) {
-    // Can not expand beyond max capacity
-    return false;
-  }
+  // 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);
 
-  // Expand
-  if (!_backing.expand(_capacity, new_capacity)) {
-    log_error(gc)("Failed to expand Java heap with " SIZE_FORMAT "%s",
-                  byte_size_in_proper_unit(expand_with),
-                  proper_unit_for_byte_size(expand_with));
-    return false;
+  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;
   }
-
-  _capacity = new_capacity;
-
-  return true;
 }
 
 void ZPhysicalMemoryManager::nmt_commit(ZPhysicalMemory pmem, uintptr_t offset) {
@@ -144,7 +149,7 @@
 }
 
 ZPhysicalMemory ZPhysicalMemoryManager::alloc(size_t size) {
-  if (!ensure_available(size)) {
+  if (unused_capacity() < size) {
     // Not enough memory available
     return ZPhysicalMemory();
   }
--- a/src/hotspot/share/gc/z/zPhysicalMemory.hpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/share/gc/z/zPhysicalMemory.hpp	Wed Jul 04 12:04:02 2018 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -70,11 +70,10 @@
 private:
   ZPhysicalMemoryBacking _backing;
   const size_t           _max_capacity;
+  size_t                 _current_max_capacity;
   size_t                 _capacity;
   size_t                 _used;
 
-  bool ensure_available(size_t size);
-
   void nmt_commit(ZPhysicalMemory pmem, uintptr_t offset);
   void nmt_uncommit(ZPhysicalMemory pmem, uintptr_t offset);
 
@@ -84,9 +83,11 @@
   bool is_initialized() const;
 
   size_t max_capacity() const;
+  size_t current_max_capacity() const;
   size_t capacity() const;
-  size_t used() const;
-  size_t available() const;
+  size_t unused_capacity() const;
+
+  void try_ensure_unused_capacity(size_t size);
 
   ZPhysicalMemory alloc(size_t size);
   void free(ZPhysicalMemory pmem);
--- a/src/hotspot/share/gc/z/zPhysicalMemory.inline.hpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/share/gc/z/zPhysicalMemory.inline.hpp	Wed Jul 04 12:04:02 2018 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -71,16 +71,16 @@
   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::used() const {
-  return _used;
-}
-
-inline size_t ZPhysicalMemoryManager::available() const {
-  return _max_capacity - _used;
+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	Wed Jul 04 16:49:34 2018 +0800
+++ b/src/hotspot/share/gc/z/zPreMappedMemory.cpp	Wed Jul 04 12:04:02 2018 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -42,22 +42,26 @@
   log_info(gc, init)("Pre-touching: %s", AlwaysPreTouch ? "Enabled" : "Disabled");
   log_info(gc, init)("Pre-mapping: " SIZE_FORMAT "M", size / M);
 
-  _pmem = pmm.alloc(size);
-  if (_pmem.is_null()) {
-    // Out of memory
-    return;
-  }
+  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
-    pmm.free(_pmem);
-    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());
   }
 
-  // Map physical memory
-  pmm.map(_pmem, _vmem.start());
-
   _initialized = true;
 }
 
--- a/test/hotspot/gtest/gc/z/test_zPhysicalMemory.cpp	Wed Jul 04 16:49:34 2018 +0800
+++ b/test/hotspot/gtest/gc/z/test_zPhysicalMemory.cpp	Wed Jul 04 12:04:02 2018 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -55,6 +55,9 @@
 
   ZPhysicalMemoryManager pmem_manager(10 * SegmentSize, SegmentSize);
 
+  pmem_manager.try_ensure_unused_capacity(10 * SegmentSize);
+  EXPECT_EQ(pmem_manager.unused_capacity(), 10 * SegmentSize);
+
   ZPhysicalMemory pmem = pmem_manager.alloc(8 * SegmentSize);
   EXPECT_EQ(pmem.nsegments(), 1u) << "wrong number of segments";