8231552: ZGC: Refine address space reservation
authorpliden
Thu, 24 Oct 2019 17:24:58 +0200
changeset 58787 32d39d9525f9
parent 58786 7909763ad193
child 58788 6a147ac7a68f
8231552: ZGC: Refine address space reservation Reviewed-by: eosterlund, stefank
src/hotspot/cpu/aarch64/gc/z/zGlobals_aarch64.cpp
src/hotspot/cpu/aarch64/gc/z/zGlobals_aarch64.hpp
src/hotspot/cpu/x86/gc/z/zGlobals_x86.cpp
src/hotspot/cpu/x86/gc/z/zGlobals_x86.hpp
src/hotspot/os/posix/gc/z/zVirtualMemory_posix.cpp
src/hotspot/share/gc/z/zAddressSpaceLimit.cpp
src/hotspot/share/gc/z/zAddressSpaceLimit.hpp
src/hotspot/share/gc/z/zArguments.cpp
src/hotspot/share/gc/z/zGlobals.hpp
src/hotspot/share/gc/z/zVirtualMemory.cpp
src/hotspot/share/gc/z/zVirtualMemory.hpp
test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/TestDescription.java
--- a/src/hotspot/cpu/aarch64/gc/z/zGlobals_aarch64.cpp	Thu Oct 24 16:37:22 2019 +0200
+++ b/src/hotspot/cpu/aarch64/gc/z/zGlobals_aarch64.cpp	Thu Oct 24 17:24:58 2019 +0200
@@ -142,8 +142,7 @@
 size_t ZPlatformAddressOffsetBits() {
   const size_t min_address_offset_bits = 42; // 4TB
   const size_t max_address_offset_bits = 44; // 16TB
-  const size_t virtual_to_physical_ratio = 7; // 7:1
-  const size_t address_offset = ZUtils::round_up_power_of_2(MaxHeapSize * virtual_to_physical_ratio);
+  const size_t address_offset = ZUtils::round_up_power_of_2(MaxHeapSize * ZVirtualToPhysicalRatio);
   const size_t address_offset_bits = log2_intptr(address_offset);
   return MIN2(MAX2(address_offset_bits, min_address_offset_bits), max_address_offset_bits);
 }
--- a/src/hotspot/cpu/aarch64/gc/z/zGlobals_aarch64.hpp	Thu Oct 24 16:37:22 2019 +0200
+++ b/src/hotspot/cpu/aarch64/gc/z/zGlobals_aarch64.hpp	Thu Oct 24 17:24:58 2019 +0200
@@ -24,23 +24,13 @@
 #ifndef CPU_AARCH64_GC_Z_ZGLOBALS_AARCH64_HPP
 #define CPU_AARCH64_GC_Z_ZGLOBALS_AARCH64_HPP
 
-//
-// Page Allocation Tiers
-// ---------------------
-//
-//  Page Type     Page Size     Object Size Limit     Object Alignment
-//  ------------------------------------------------------------------
-//  Small         2M            <= 265K               <MinObjAlignmentInBytes>
-//  Medium        32M           <= 4M                 4K
-//  Large         X*M           > 4M                  2M
-//  ------------------------------------------------------------------
-//
 const size_t ZPlatformGranuleSizeShift      = 21; // 2MB
+const size_t ZPlatformHeapViews             = 3;
 const size_t ZPlatformNMethodDisarmedOffset = 4;
 const size_t ZPlatformCacheLineSize         = 64;
 
-uintptr_t    ZPlatformAddressBase();
-size_t       ZPlatformAddressOffsetBits();
-size_t       ZPlatformAddressMetadataShift();
+uintptr_t ZPlatformAddressBase();
+size_t ZPlatformAddressOffsetBits();
+size_t ZPlatformAddressMetadataShift();
 
 #endif // CPU_AARCH64_GC_Z_ZGLOBALS_AARCH64_HPP
--- a/src/hotspot/cpu/x86/gc/z/zGlobals_x86.cpp	Thu Oct 24 16:37:22 2019 +0200
+++ b/src/hotspot/cpu/x86/gc/z/zGlobals_x86.cpp	Thu Oct 24 17:24:58 2019 +0200
@@ -142,8 +142,7 @@
 size_t ZPlatformAddressOffsetBits() {
   const size_t min_address_offset_bits = 42; // 4TB
   const size_t max_address_offset_bits = 44; // 16TB
-  const size_t virtual_to_physical_ratio = 7; // 7:1
-  const size_t address_offset = ZUtils::round_up_power_of_2(MaxHeapSize * virtual_to_physical_ratio);
+  const size_t address_offset = ZUtils::round_up_power_of_2(MaxHeapSize * ZVirtualToPhysicalRatio);
   const size_t address_offset_bits = log2_intptr(address_offset);
   return MIN2(MAX2(address_offset_bits, min_address_offset_bits), max_address_offset_bits);
 }
--- a/src/hotspot/cpu/x86/gc/z/zGlobals_x86.hpp	Thu Oct 24 16:37:22 2019 +0200
+++ b/src/hotspot/cpu/x86/gc/z/zGlobals_x86.hpp	Thu Oct 24 17:24:58 2019 +0200
@@ -24,23 +24,13 @@
 #ifndef CPU_X86_GC_Z_ZGLOBALS_X86_HPP
 #define CPU_X86_GC_Z_ZGLOBALS_X86_HPP
 
-//
-// Page Allocation Tiers
-// ---------------------
-//
-//  Page Type     Page Size     Object Size Limit     Object Alignment
-//  ------------------------------------------------------------------
-//  Small         2M            <= 265K               <MinObjAlignmentInBytes>
-//  Medium        32M           <= 4M                 4K
-//  Large         X*M           > 4M                  2M
-//  ------------------------------------------------------------------
-//
 const size_t ZPlatformGranuleSizeShift      = 21; // 2MB
+const size_t ZPlatformHeapViews             = 3;
 const size_t ZPlatformNMethodDisarmedOffset = 4;
 const size_t ZPlatformCacheLineSize         = 64;
 
-uintptr_t    ZPlatformAddressBase();
-size_t       ZPlatformAddressOffsetBits();
-size_t       ZPlatformAddressMetadataShift();
+uintptr_t ZPlatformAddressBase();
+size_t ZPlatformAddressOffsetBits();
+size_t ZPlatformAddressMetadataShift();
 
 #endif // CPU_X86_GC_Z_ZGLOBALS_X86_HPP
--- a/src/hotspot/os/posix/gc/z/zVirtualMemory_posix.cpp	Thu Oct 24 16:37:22 2019 +0200
+++ b/src/hotspot/os/posix/gc/z/zVirtualMemory_posix.cpp	Thu Oct 24 17:24:58 2019 +0200
@@ -51,7 +51,7 @@
   return true;
 }
 
-bool ZVirtualMemoryManager::reserve_platform(uintptr_t start, size_t size) {
+bool ZVirtualMemoryManager::reserve_contiguous_platform(uintptr_t start, size_t size) {
   // Reserve address views
   const uintptr_t marked0 = ZAddress::marked0(start);
   const uintptr_t marked1 = ZAddress::marked1(start);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/z/zAddressSpaceLimit.cpp	Thu Oct 24 17:24:58 2019 +0200
@@ -0,0 +1,51 @@
+/*
+ * 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/zAddressSpaceLimit.hpp"
+#include "gc/z/zGlobals.hpp"
+#include "runtime/os.hpp"
+#include "utilities/align.hpp"
+
+static size_t address_space_limit() {
+  julong limit = 0;
+
+  if (os::has_allocatable_memory_limit(&limit)) {
+    return (size_t)limit;
+  }
+
+  // No limit
+  return SIZE_MAX;
+}
+
+size_t ZAddressSpaceLimit::mark_stack() {
+  // Allow mark stacks to occupy 10% of the address space
+  const size_t limit = address_space_limit() / 10;
+  return align_up(limit, ZMarkStackSpaceExpandSize);
+}
+
+size_t ZAddressSpaceLimit::heap_view() {
+  // Allow all heap views to occupy 50% of the address space
+  const size_t limit = address_space_limit() / 2 / ZHeapViews;
+  return align_up(limit, ZGranuleSize);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/z/zAddressSpaceLimit.hpp	Thu Oct 24 17:24:58 2019 +0200
@@ -0,0 +1,35 @@
+/*
+ * 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_ZADDRESSSPACELIMIT_HPP
+#define SHARE_GC_Z_ZADDRESSSPACELIMIT_HPP
+
+#include "memory/allocation.hpp"
+
+class ZAddressSpaceLimit : public AllStatic {
+public:
+  static size_t mark_stack();
+  static size_t heap_view();
+};
+
+#endif // SHARE_GC_Z_ZADDRESSSPACELIMIT_HPP
--- a/src/hotspot/share/gc/z/zArguments.cpp	Thu Oct 24 16:37:22 2019 +0200
+++ b/src/hotspot/share/gc/z/zArguments.cpp	Thu Oct 24 17:24:58 2019 +0200
@@ -22,6 +22,7 @@
  */
 
 #include "precompiled.hpp"
+#include "gc/z/zAddressSpaceLimit.hpp"
 #include "gc/z/zArguments.hpp"
 #include "gc/z/zCollectedHeap.hpp"
 #include "gc/z/zWorkers.hpp"
@@ -37,6 +38,15 @@
 void ZArguments::initialize() {
   GCArguments::initialize();
 
+  // Check mark stack size
+  const size_t mark_stack_space_limit = ZAddressSpaceLimit::mark_stack();
+  if (ZMarkStackSpaceLimit > mark_stack_space_limit) {
+    if (!FLAG_IS_DEFAULT(ZMarkStackSpaceLimit)) {
+      vm_exit_during_initialization("ZMarkStackSpaceLimit too large for limited address space");
+    }
+    FLAG_SET_DEFAULT(ZMarkStackSpaceLimit, mark_stack_space_limit);
+  }
+
   // Enable NUMA by default
   if (FLAG_IS_DEFAULT(UseNUMA)) {
     FLAG_SET_DEFAULT(UseNUMA, true);
--- a/src/hotspot/share/gc/z/zGlobals.hpp	Thu Oct 24 16:37:22 2019 +0200
+++ b/src/hotspot/share/gc/z/zGlobals.hpp	Thu Oct 24 17:24:58 2019 +0200
@@ -44,6 +44,23 @@
 const size_t      ZGranuleSizeShift             = ZPlatformGranuleSizeShift;
 const size_t      ZGranuleSize                  = (size_t)1 << ZGranuleSizeShift;
 
+// Number of heap views
+const size_t      ZHeapViews                    = ZPlatformHeapViews;
+
+// Virtual memory to physical memory ratio
+const size_t      ZVirtualToPhysicalRatio       = 16; // 16:1
+
+//
+// Page Tiers (assuming ZGranuleSize=2M)
+// -------------------------------------
+//
+//                 Page Size        Object Size      Object Alignment
+//                 --------------------------------------------------
+//  Small          2M               <= 265K          MinObjAlignmentInBytes
+//  Medium         32M              <= 4M            4K
+//  Large          N x 2M           > 4M             2M
+//
+
 // Page types
 const uint8_t     ZPageTypeSmall                = 0;
 const uint8_t     ZPageTypeMedium               = 1;
--- a/src/hotspot/share/gc/z/zVirtualMemory.cpp	Thu Oct 24 16:37:22 2019 +0200
+++ b/src/hotspot/share/gc/z/zVirtualMemory.cpp	Thu Oct 24 17:24:58 2019 +0200
@@ -22,10 +22,13 @@
  */
 
 #include "precompiled.hpp"
+#include "gc/z/zAddressSpaceLimit.hpp"
 #include "gc/z/zGlobals.hpp"
 #include "gc/z/zVirtualMemory.inline.hpp"
 #include "logging/log.hpp"
 #include "services/memTracker.hpp"
+#include "utilities/debug.hpp"
+#include "utilities/align.hpp"
 
 ZVirtualMemoryManager::ZVirtualMemoryManager(size_t max_capacity) :
     _manager(),
@@ -38,11 +41,9 @@
     return;
   }
 
-  log_info(gc, init)("Address Space: " SIZE_FORMAT "T", ZAddressOffsetMax / K / G);
-
   // Reserve address space
-  if (reserve(0, ZAddressOffsetMax) < max_capacity) {
-    log_error(gc)("Failed to reserve address space for Java heap");
+  if (!reserve(max_capacity)) {
+    log_error(gc)("Failed to reserve enough address space for Java heap");
     return;
   }
 
@@ -50,21 +51,92 @@
   _initialized = true;
 }
 
-size_t ZVirtualMemoryManager::reserve(uintptr_t start, size_t size) {
-  if (size < ZGranuleSize) {
+size_t ZVirtualMemoryManager::reserve_discontiguous(uintptr_t start, size_t size, size_t min_range) {
+  if (size < min_range) {
+    // Too small
+    return 0;
+  }
+
+  assert(is_aligned(size, ZGranuleSize), "Misaligned");
+
+  if (reserve_contiguous_platform(start, size)) {
+    // Make the address range free
+    _manager.free(start, size);
+    return size;
+  }
+
+  const size_t half = size / 2;
+  if (half < min_range) {
     // Too small
     return 0;
   }
 
-  if (!reserve_platform(start, size)) {
-    const size_t half = size / 2;
-    return reserve(start, half) + reserve(start + half, half);
+  // Divide and conquer
+  const size_t first_part = align_down(half, ZGranuleSize);
+  const size_t second_part = size - first_part;
+  return reserve_discontiguous(start, first_part, min_range) +
+         reserve_discontiguous(start + first_part, second_part, min_range);
+}
+
+size_t ZVirtualMemoryManager::reserve_discontiguous(size_t size) {
+  // Don't try to reserve address ranges smaller than 1% of the requested size.
+  // This avoids an explosion of reservation attempts in case large parts of the
+  // address space is already occupied.
+  const size_t min_range = align_up(size / 100, ZGranuleSize);
+  size_t start = 0;
+  size_t reserved = 0;
+
+  // Reserve size somewhere between [0, ZAddressOffsetMax)
+  while (reserved < size && start < ZAddressOffsetMax) {
+    const size_t remaining = MIN2(size - reserved, ZAddressOffsetMax - start);
+    reserved += reserve_discontiguous(start, remaining, min_range);
+    start += remaining;
   }
 
-  // Make the address range free
-  _manager.free(start, size);
+  return reserved;
+}
+
+bool ZVirtualMemoryManager::reserve_contiguous(size_t size) {
+  // Allow at most 8192 attempts spread evenly across [0, ZAddressOffsetMax)
+  const size_t end = ZAddressOffsetMax - size;
+  const size_t increment = align_up(end / 8192, ZGranuleSize);
+
+  for (size_t start = 0; start <= end; start += increment) {
+    if (reserve_contiguous_platform(start, size)) {
+      // Make the address range free
+      _manager.free(start, size);
+
+      // Success
+      return true;
+    }
+  }
+
+  // Failed
+  return false;
+}
 
-  return size;
+bool ZVirtualMemoryManager::reserve(size_t max_capacity) {
+  const size_t limit = MIN2(ZAddressOffsetMax, ZAddressSpaceLimit::heap_view());
+  const size_t size = MIN2(max_capacity * ZVirtualToPhysicalRatio, limit);
+
+  size_t reserved = size;
+  bool contiguous = true;
+
+  // Prefer a contiguous address space
+  if (!reserve_contiguous(size)) {
+    // Fall back to a discontiguous address space
+    reserved = reserve_discontiguous(size);
+    contiguous = false;
+  }
+
+  log_info(gc, init)("Address Space Type: %s/%s/%s",
+                     (contiguous ? "Contiguous" : "Discontiguous"),
+                     (limit == ZAddressOffsetMax ? "Unrestricted" : "Restricted"),
+                     (reserved == size ? "Complete" : "Degraded"));
+  log_info(gc, init)("Address Space Size: " SIZE_FORMAT "M x " SIZE_FORMAT " = " SIZE_FORMAT "M",
+                     reserved / M, ZHeapViews, (reserved * ZHeapViews) / M);
+
+  return reserved >= max_capacity;
 }
 
 void ZVirtualMemoryManager::nmt_reserve(uintptr_t start, size_t size) {
--- a/src/hotspot/share/gc/z/zVirtualMemory.hpp	Thu Oct 24 16:37:22 2019 +0200
+++ b/src/hotspot/share/gc/z/zVirtualMemory.hpp	Thu Oct 24 17:24:58 2019 +0200
@@ -50,8 +50,12 @@
   ZMemoryManager _manager;
   bool           _initialized;
 
-  bool reserve_platform(uintptr_t start, size_t size);
-  size_t reserve(uintptr_t start, size_t size);
+  bool reserve_contiguous_platform(uintptr_t start, size_t size);
+  bool reserve_contiguous(size_t size);
+  size_t reserve_discontiguous(uintptr_t start, size_t size, size_t min_range);
+  size_t reserve_discontiguous(size_t size);
+  bool reserve(size_t max_capacity);
+
   void nmt_reserve(uintptr_t start, size_t size);
 
 public:
--- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/TestDescription.java	Thu Oct 24 16:37:22 2019 +0200
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/TestDescription.java	Thu Oct 24 17:24:58 2019 +0200
@@ -42,8 +42,6 @@
  * @library /vmTestbase
  *          /test/lib
  * @requires os.family != "aix"
- * @comment Test is incompatible with ZGC, due to ZGC's address space requirements.
- * @requires vm.gc != "Z"
  * @run driver jdk.test.lib.FileInstaller . .
  * @build nsk.jvmti.Allocate.alloc001
  * @run shell alloc001.sh