8220587: ZGC: Break out forwarding information from ZPage
authorpliden
Mon, 18 Mar 2019 11:50:39 +0100
changeset 54162 f344a0c6e19e
parent 54161 349843ebb209
child 54163 790679f86a51
8220587: ZGC: Break out forwarding information from ZPage Reviewed-by: stefank, eosterlund
src/hotspot/share/gc/z/vmStructs_z.hpp
src/hotspot/share/gc/z/zBarrier.cpp
src/hotspot/share/gc/z/zForwarding.cpp
src/hotspot/share/gc/z/zForwarding.hpp
src/hotspot/share/gc/z/zForwarding.inline.hpp
src/hotspot/share/gc/z/zForwardingEntry.hpp
src/hotspot/share/gc/z/zForwardingTable.cpp
src/hotspot/share/gc/z/zForwardingTable.hpp
src/hotspot/share/gc/z/zForwardingTable.inline.hpp
src/hotspot/share/gc/z/zForwardingTableEntry.hpp
src/hotspot/share/gc/z/zGranuleMap.hpp
src/hotspot/share/gc/z/zGranuleMap.inline.hpp
src/hotspot/share/gc/z/zHeap.cpp
src/hotspot/share/gc/z/zHeap.hpp
src/hotspot/share/gc/z/zHeap.inline.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/zPageCache.cpp
src/hotspot/share/gc/z/zRelocate.cpp
src/hotspot/share/gc/z/zRelocate.hpp
test/hotspot/gtest/gc/z/test_zForwarding.cpp
test/hotspot/gtest/gc/z/test_zForwardingTable.cpp
test/hotspot/jtreg/ProblemList-zgc.txt
--- a/src/hotspot/share/gc/z/vmStructs_z.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/vmStructs_z.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -25,6 +25,7 @@
 #define SHARE_GC_Z_VMSTRUCTS_Z_HPP
 
 #include "gc/z/zCollectedHeap.hpp"
+#include "gc/z/zForwarding.hpp"
 #include "gc/z/zGranuleMap.hpp"
 #include "gc/z/zHeap.hpp"
 #include "gc/z/zPageAllocator.hpp"
@@ -73,8 +74,6 @@
   nonstatic_field(ZPage,                        _seqnum,              uint32_t)                      \
   nonstatic_field(ZPage,                        _virtual,             const ZVirtualMemory)          \
   volatile_nonstatic_field(ZPage,               _top,                 uintptr_t)                     \
-  volatile_nonstatic_field(ZPage,               _refcount,            uint32_t)                      \
-  nonstatic_field(ZPage,                        _forwarding,          ZForwardingTable)              \
                                                                                                      \
   nonstatic_field(ZPageAllocator,               _physical,            ZPhysicalMemoryManager)        \
   nonstatic_field(ZPageAllocator,               _used,                size_t)                        \
@@ -86,8 +85,7 @@
   nonstatic_field(ZVirtualMemory,               _start,               uintptr_t)                     \
   nonstatic_field(ZVirtualMemory,               _end,                 uintptr_t)                     \
                                                                                                      \
-  nonstatic_field(ZForwardingTable,             _table,               ZForwardingTableEntry*)        \
-  nonstatic_field(ZForwardingTable,             _size,                size_t)                        \
+  nonstatic_field(ZForwarding,                  _nentries,            const uint32_t)                \
                                                                                                      \
   nonstatic_field(ZPhysicalMemoryManager,       _max_capacity,        const size_t)                  \
   nonstatic_field(ZPhysicalMemoryManager,       _capacity,            size_t)
@@ -121,7 +119,8 @@
   declare_toplevel_type(ZGranuleMapForPageTable)                                                     \
   declare_toplevel_type(ZVirtualMemory)                                                              \
   declare_toplevel_type(ZForwardingTable)                                                            \
-  declare_toplevel_type(ZForwardingTableEntry)                                                       \
+  declare_toplevel_type(ZForwarding)                                                                 \
+  declare_toplevel_type(ZForwardingEntry)                                                            \
   declare_toplevel_type(ZPhysicalMemoryManager)
 
 #endif // SHARE_GC_Z_VMSTRUCTS_Z_HPP
--- a/src/hotspot/share/gc/z/zBarrier.cpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zBarrier.cpp	Mon Mar 18 11:50:39 2019 +0100
@@ -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
@@ -98,27 +98,13 @@
 uintptr_t ZBarrier::remap(uintptr_t addr) {
   assert(!ZAddress::is_good(addr), "Should not be good");
   assert(!ZAddress::is_weak_good(addr), "Should not be weak good");
-
-  if (ZHeap::heap()->is_relocating(addr)) {
-    // Forward
-    return ZHeap::heap()->forward_object(addr);
-  }
-
-  // Remap
-  return ZAddress::good(addr);
+  return ZHeap::heap()->remap_object(addr);
 }
 
 uintptr_t ZBarrier::relocate(uintptr_t addr) {
   assert(!ZAddress::is_good(addr), "Should not be good");
   assert(!ZAddress::is_weak_good(addr), "Should not be weak good");
-
-  if (ZHeap::heap()->is_relocating(addr)) {
-    // Relocate
-    return ZHeap::heap()->relocate_object(addr);
-  }
-
-  // Remap
-  return ZAddress::good(addr);
+  return ZHeap::heap()->relocate_object(addr);
 }
 
 uintptr_t ZBarrier::relocate_or_mark(uintptr_t addr) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/z/zForwarding.cpp	Mon Mar 18 11:50:39 2019 +0100
@@ -0,0 +1,82 @@
+/*
+ * 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
+ * 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/zForwarding.inline.hpp"
+#include "gc/z/zUtils.inline.hpp"
+#include "memory/allocation.hpp"
+#include "utilities/debug.hpp"
+
+ZForwarding::ZForwarding(uintptr_t start, size_t object_alignment_shift, uint32_t nentries) :
+    _start(start),
+    _object_alignment_shift(object_alignment_shift),
+    _nentries(nentries),
+    _refcount(1),
+    _pinned(false) {}
+
+ZForwarding* ZForwarding::create(uintptr_t start, size_t object_alignment_shift, uint32_t live_objects) {
+  assert(live_objects > 0, "Invalid value");
+
+  // Allocate table for linear probing. The size of the table must be
+  // a power of two to allow for quick and inexpensive indexing/masking.
+  // The table is sized to have a load factor of 50%, i.e. sized to have
+  // double the number of entries actually inserted.
+  const uint32_t nentries = ZUtils::round_up_power_of_2(live_objects * 2);
+
+  const size_t size = sizeof(ZForwarding) + (sizeof(ZForwardingEntry) * nentries);
+  uint8_t* const addr = NEW_C_HEAP_ARRAY(uint8_t, size, mtGC);
+  ZForwardingEntry* const entries = ::new (addr + sizeof(ZForwarding)) ZForwardingEntry[nentries];
+  ZForwarding* const forwarding = ::new (addr) ZForwarding(start, object_alignment_shift, nentries);
+  return forwarding;
+}
+
+void ZForwarding::destroy(ZForwarding* forwarding) {
+  FREE_C_HEAP_ARRAY(uint8_t, forwarding);
+}
+
+void ZForwarding::verify(uint32_t object_max_count, uint32_t live_objects) const {
+  uint32_t count = 0;
+
+  for (ZForwardingCursor i = 0; i < _nentries; i++) {
+    const ZForwardingEntry entry = at(&i);
+    if (entry.is_empty()) {
+      // Skip empty entries
+      continue;
+    }
+
+    // Check from index
+    guarantee(entry.from_index() < object_max_count, "Invalid from index");
+
+    // Check for duplicates
+    for (ZForwardingCursor j = i + 1; j < _nentries; j++) {
+      const ZForwardingEntry other = at(&j);
+      guarantee(entry.from_index() != other.from_index(), "Duplicate from");
+      guarantee(entry.to_offset() != other.to_offset(), "Duplicate to");
+    }
+
+    count++;
+  }
+
+  // Check number of non-null entries
+  guarantee(live_objects == count, "Count mismatch");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/z/zForwarding.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -0,0 +1,69 @@
+/*
+ * 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
+ * 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_ZFORWARDING_HPP
+#define SHARE_GC_Z_ZFORWARDING_HPP
+
+#include "gc/z/zForwardingEntry.hpp"
+
+typedef uint32_t ZForwardingCursor;
+
+class ZForwarding {
+  friend class VMStructs;
+  friend class ZForwardingTest;
+
+private:
+  const uintptr_t   _start;
+  const size_t      _object_alignment_shift;
+  const uint32_t    _nentries;
+  volatile uint32_t _refcount;
+  volatile bool     _pinned;
+
+  ZForwardingEntry* entries() const;
+  ZForwardingEntry at(ZForwardingCursor* cursor) const;
+  ZForwardingEntry first(uintptr_t from_index, ZForwardingCursor* cursor) const;
+  ZForwardingEntry next(ZForwardingCursor* cursor) const;
+
+  ZForwarding(uintptr_t start, size_t object_alignment_shift, uint32_t nentries);
+
+public:
+  static ZForwarding* create(uintptr_t start, size_t object_alignment_shift, uint32_t live_objects);
+  static void destroy(ZForwarding* forwarding);
+
+  uintptr_t start() const;
+  size_t object_alignment_shift() const;
+
+  bool inc_refcount();
+  bool dec_refcount();
+
+  bool is_pinned() const;
+  void set_pinned();
+
+  ZForwardingEntry find(uintptr_t from_index) const;
+  ZForwardingEntry find(uintptr_t from_index, ZForwardingCursor* cursor) const;
+  uintptr_t insert(uintptr_t from_index, uintptr_t to_offset, ZForwardingCursor* cursor);
+
+  void verify(uint32_t object_max_count, uint32_t live_objects) const;
+};
+
+#endif // SHARE_GC_Z_ZFORWARDING_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/z/zForwarding.inline.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -0,0 +1,139 @@
+/*
+ * 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
+ * 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_ZFORWARDING_INLINE_HPP
+#define SHARE_GC_Z_ZFORWARDING_INLINE_HPP
+
+#include "gc/z/zForwarding.hpp"
+#include "gc/z/zGlobals.hpp"
+#include "gc/z/zHash.inline.hpp"
+#include "runtime/atomic.hpp"
+#include "utilities/debug.hpp"
+
+inline uintptr_t ZForwarding::start() const {
+  return _start;
+}
+
+inline size_t ZForwarding::object_alignment_shift() const {
+  return _object_alignment_shift;
+}
+
+inline bool ZForwarding::inc_refcount() {
+  uint32_t refcount = Atomic::load(&_refcount);
+
+  while (refcount > 0) {
+    const uint32_t old_refcount = refcount;
+    const uint32_t new_refcount = old_refcount + 1;
+    const uint32_t prev_refcount = Atomic::cmpxchg(new_refcount, &_refcount, old_refcount);
+    if (prev_refcount == old_refcount) {
+      return true;
+    }
+
+    refcount = prev_refcount;
+  }
+
+  return false;
+}
+
+inline bool ZForwarding::dec_refcount() {
+  assert(_refcount > 0, "Invalid state");
+  return Atomic::sub(1u, &_refcount) == 0u;
+}
+
+inline bool ZForwarding::is_pinned() const {
+  return Atomic::load(&_pinned);
+}
+
+inline void ZForwarding::set_pinned() {
+  Atomic::store(true, &_pinned);
+}
+
+inline ZForwardingEntry* ZForwarding::entries() const {
+  return reinterpret_cast<ZForwardingEntry*>(reinterpret_cast<uintptr_t>(this) + sizeof(*this));
+}
+
+inline ZForwardingEntry ZForwarding::at(ZForwardingCursor* cursor) const {
+  return Atomic::load(entries() + *cursor);
+}
+
+inline ZForwardingEntry ZForwarding::first(uintptr_t from_index, ZForwardingCursor* cursor) const {
+  const uint32_t mask = _nentries - 1;
+  const uint32_t hash = ZHash::uint32_to_uint32((uint32_t)from_index);
+  *cursor = hash & mask;
+  return at(cursor);
+}
+
+inline ZForwardingEntry ZForwarding::next(ZForwardingCursor* cursor) const {
+  const uint32_t mask = _nentries - 1;
+  *cursor = (*cursor + 1) & mask;
+  return at(cursor);
+}
+
+inline ZForwardingEntry ZForwarding::find(uintptr_t from_index) const {
+  ZForwardingCursor dummy;
+  return find(from_index, &dummy);
+}
+
+inline ZForwardingEntry ZForwarding::find(uintptr_t from_index, ZForwardingCursor* cursor) const {
+  // Reading entries in the table races with the atomic CAS done for
+  // insertion into the table. This is safe because each entry is at
+  // most updated once (from -1 to something else).
+  ZForwardingEntry entry = first(from_index, cursor);
+  while (!entry.is_empty()) {
+    if (entry.from_index() == from_index) {
+      // Match found, return matching entry
+      return entry;
+    }
+
+    entry = next(cursor);
+  }
+
+  // Match not found, return empty entry
+  return entry;
+}
+
+inline uintptr_t ZForwarding::insert(uintptr_t from_index, uintptr_t to_offset, ZForwardingCursor* cursor) {
+  const ZForwardingEntry new_entry(from_index, to_offset);
+  const ZForwardingEntry old_entry; // empty
+
+  for (;;) {
+    const ZForwardingEntry prev_entry = Atomic::cmpxchg(new_entry, entries() + *cursor, old_entry);
+    if (prev_entry.is_empty()) {
+      // Success
+      return to_offset;
+    }
+
+    // Find next empty or matching entry
+    ZForwardingEntry entry = at(cursor);
+    while (!entry.is_empty()) {
+      if (entry.from_index() == from_index) {
+        // Match found, return already inserted address
+        return entry.to_offset();
+      }
+
+      entry = next(cursor);
+    }
+  }
+}
+
+#endif // SHARE_GC_Z_ZFORWARDING_INLINE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/z/zForwardingEntry.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2017, 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_ZFORWARDINGENTRY_HPP
+#define SHARE_GC_Z_ZFORWARDINGENTRY_HPP
+
+#include "gc/z/zBitField.hpp"
+#include "memory/allocation.hpp"
+#include "metaprogramming/primitiveConversions.hpp"
+
+//
+// Forwarding entry layout
+// -----------------------
+//
+//   6                      4 4                                             0
+//   3                      2 1                                             0
+//  +------------------------+-----------------------------------------------+
+//  |11111111 11111111 111111|11 11111111 11111111 11111111 11111111 11111111|
+//  +------------------------+-----------------------------------------------+
+//  |                        |
+//  |                        * 41-0 To Object Offset (42-bits)
+//  |
+//  * 63-42 From Object Index (22-bits)
+//
+
+class ZForwardingEntry {
+  friend struct PrimitiveConversions;
+
+private:
+  typedef ZBitField<uint64_t, size_t, 0,  42> field_to_offset;
+  typedef ZBitField<uint64_t, size_t, 42, 22> field_from_index;
+
+  uint64_t _entry;
+
+  static uintptr_t empty() {
+    return (uintptr_t)-1;
+  }
+
+public:
+  ZForwardingEntry() :
+      _entry(empty()) {}
+
+  ZForwardingEntry(size_t from_index, size_t to_offset) :
+      _entry(field_from_index::encode(from_index) |
+             field_to_offset::encode(to_offset)) {}
+
+  bool is_empty() const {
+    return _entry == empty();
+  }
+
+  size_t to_offset() const {
+    return field_to_offset::decode(_entry);
+  }
+
+  size_t from_index() const {
+    return field_from_index::decode(_entry);
+  }
+};
+
+// Needed to allow atomic operations on ZForwardingEntry
+template <>
+struct PrimitiveConversions::Translate<ZForwardingEntry> : public TrueType {
+  typedef ZForwardingEntry Value;
+  typedef uint64_t         Decayed;
+
+  static Decayed decay(Value v) {
+    return v._entry;
+  }
+
+  static Value recover(Decayed d) {
+    ZForwardingEntry entry;
+    entry._entry = d;
+    return entry;
+  }
+};
+
+#endif // SHARE_GC_Z_ZFORWARDINGENTRY_HPP
--- a/src/hotspot/share/gc/z/zForwardingTable.cpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zForwardingTable.cpp	Mon Mar 18 11:50:39 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -22,63 +22,49 @@
  */
 
 #include "precompiled.hpp"
+#include "gc/z/zForwarding.hpp"
 #include "gc/z/zForwardingTable.inline.hpp"
-#include "gc/z/zUtils.inline.hpp"
-#include "memory/allocation.inline.hpp"
+#include "gc/z/zGranuleMap.inline.hpp"
 #include "utilities/debug.hpp"
 
-void ZForwardingTable::setup(size_t live_objects) {
-  assert(is_null(), "Should be empty");
-  assert(live_objects > 0, "Invalid size");
+ZForwardingTable::ZForwardingTable() :
+    _map() {}
 
-  // Allocate table for linear probing. The size of the table must be
-  // a power of two to allow for quick and inexpensive indexing/masking.
-  // The table is sized to have a load factor of 50%, i.e. sized to have
-  // double the number of entries actually inserted.
-  _size = ZUtils::round_up_power_of_2(live_objects * 2);
-  _table = MallocArrayAllocator<ZForwardingTableEntry>::allocate(_size, mtGC);
+void ZForwardingTable::insert(uintptr_t start,
+                              size_t size,
+                              size_t object_alignment_shift,
+                              uint32_t live_objects) {
+  // Allocate forwarding
+  ZForwarding* const forwarding = ZForwarding::create(start,
+                                                      object_alignment_shift,
+                                                      live_objects);
 
-  // Construct table entries
-  for (size_t i = 0; i < _size; i++) {
-    ::new (_table + i) ZForwardingTableEntry();
-  }
+  // Insert into forwarding table
+  const uintptr_t addr = ZAddress::good(start);
+  assert(get(addr) == NULL, "Invalid entry");
+  _map.put(addr, size, forwarding);
 }
 
-void ZForwardingTable::reset() {
-  // Destruct table entries
-  for (size_t i = 0; i < _size; i++) {
-    (_table + i)->~ZForwardingTableEntry();
-  }
+void ZForwardingTable::clear() {
+  ZForwarding* prev_forwarding = NULL;
 
-  // Free table
-  MallocArrayAllocator<ZForwardingTableEntry>::free(_table);
-  _table = NULL;
-  _size = 0;
-}
-
-void ZForwardingTable::verify(size_t object_max_count, size_t live_objects) const {
-  size_t count = 0;
-
-  for (size_t i = 0; i < _size; i++) {
-    const ZForwardingTableEntry entry = _table[i];
-    if (entry.is_empty()) {
-      // Skip empty entries
+  // Clear and destroy all non-NULL entries
+  ZGranuleMapIterator<ZForwarding*> iter(&_map);
+  for (ZForwarding** entry; iter.next(&entry);) {
+    ZForwarding* const forwarding = *entry;
+    if (forwarding == NULL) {
+      // Skip entry
       continue;
     }
 
-    // Check from index
-    guarantee(entry.from_index() < object_max_count, "Invalid from index");
+    // Clear entry
+    *entry = NULL;
 
-    // Check for duplicates
-    for (size_t j = i + 1; j < _size; j++) {
-      const ZForwardingTableEntry other = _table[j];
-      guarantee(entry.from_index() != other.from_index(), "Duplicate from");
-      guarantee(entry.to_offset() != other.to_offset(), "Duplicate to");
+    // More than one entry can point to the same
+    // forwarding. Make sure we only destroy it once.
+    if (forwarding != prev_forwarding) {
+      ZForwarding::destroy(forwarding);
+      prev_forwarding = forwarding;
     }
-
-    count++;
   }
-
-  // Check number of non-null entries
-  guarantee(live_objects == count, "Count mismatch");
 }
--- a/src/hotspot/share/gc/z/zForwardingTable.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zForwardingTable.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -24,36 +24,24 @@
 #ifndef SHARE_GC_Z_ZFORWARDINGTABLE_HPP
 #define SHARE_GC_Z_ZFORWARDINGTABLE_HPP
 
-#include "gc/z/zForwardingTableEntry.hpp"
-#include "memory/allocation.hpp"
+#include "gc/z/zGranuleMap.hpp"
 
-typedef size_t ZForwardingTableCursor;
+class ZForwarding;
 
 class ZForwardingTable {
-  friend class VMStructs;
-  friend class ZForwardingTableTest;
-
 private:
-  ZForwardingTableEntry* _table;
-  size_t                 _size;
-
-  ZForwardingTableEntry at(ZForwardingTableCursor* cursor) const;
-  ZForwardingTableEntry first(uintptr_t from_index, ZForwardingTableCursor* cursor) const;
-  ZForwardingTableEntry next(ZForwardingTableCursor* cursor) const;
+  ZGranuleMap<ZForwarding*> _map;
 
 public:
   ZForwardingTable();
-  ~ZForwardingTable();
 
-  bool is_null() const;
-  void setup(size_t live_objects);
-  void reset();
+  ZForwarding* get(uintptr_t addr) const;
 
-  ZForwardingTableEntry find(uintptr_t from_index) const;
-  ZForwardingTableEntry find(uintptr_t from_index, ZForwardingTableCursor* cursor) const;
-  uintptr_t insert(uintptr_t from_index, uintptr_t to_offset, ZForwardingTableCursor* cursor);
-
-  void verify(size_t object_max_count, size_t live_objects) const;
+  void insert(uintptr_t start,
+              size_t size,
+              size_t object_alignment_shift,
+              uint32_t live_objects);
+  void clear();
 };
 
 #endif // SHARE_GC_Z_ZFORWARDINGTABLE_HPP
--- a/src/hotspot/share/gc/z/zForwardingTable.inline.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zForwardingTable.inline.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -25,85 +25,10 @@
 #define SHARE_GC_Z_ZFORWARDINGTABLE_INLINE_HPP
 
 #include "gc/z/zForwardingTable.hpp"
-#include "gc/z/zGlobals.hpp"
-#include "gc/z/zHash.inline.hpp"
-#include "runtime/atomic.hpp"
-#include "utilities/debug.hpp"
-
-inline ZForwardingTable::ZForwardingTable() :
-    _table(NULL),
-    _size(0) {}
-
-inline ZForwardingTable::~ZForwardingTable() {
-  assert(is_null(), "Should be empty");
-}
-
-inline ZForwardingTableEntry ZForwardingTable::at(ZForwardingTableCursor* cursor) const {
-  return _table[*cursor];
-}
-
-inline ZForwardingTableEntry ZForwardingTable::first(uintptr_t from_index, ZForwardingTableCursor* cursor) const {
-  const size_t mask = _size - 1;
-  const size_t hash = ZHash::uint32_to_uint32((uint32_t)from_index);
-  *cursor = hash & mask;
-  return at(cursor);
-}
-
-inline ZForwardingTableEntry ZForwardingTable::next(ZForwardingTableCursor* cursor) const {
-  const size_t mask = _size - 1;
-  *cursor = (*cursor + 1) & mask;
-  return at(cursor);
-}
-
-inline bool ZForwardingTable::is_null() const {
-  return _table == NULL;
-}
-
-inline ZForwardingTableEntry ZForwardingTable::find(uintptr_t from_index) const {
-  ZForwardingTableCursor dummy;
-  return find(from_index, &dummy);
-}
+#include "gc/z/zGranuleMap.inline.hpp"
 
-inline ZForwardingTableEntry ZForwardingTable::find(uintptr_t from_index, ZForwardingTableCursor* cursor) const {
-  // Reading entries in the table races with the atomic CAS done for
-  // insertion into the table. This is safe because each entry is at
-  // most updated once (from -1 to something else).
-  ZForwardingTableEntry entry = first(from_index, cursor);
-  while (!entry.is_empty()) {
-    if (entry.from_index() == from_index) {
-      // Match found, return matching entry
-      return entry;
-    }
-
-    entry = next(cursor);
-  }
-
-  // Match not found, return empty entry
-  return entry;
-}
-
-inline uintptr_t ZForwardingTable::insert(uintptr_t from_index, uintptr_t to_offset, ZForwardingTableCursor* cursor) {
-  const ZForwardingTableEntry new_entry(from_index, to_offset);
-  const ZForwardingTableEntry old_entry; // empty
-
-  for (;;) {
-    const ZForwardingTableEntry prev_entry = Atomic::cmpxchg(new_entry, _table + *cursor, old_entry);
-    if (prev_entry.is_empty()) {
-      // Success
-      return to_offset;
-    }
-
-    // Find next empty or matching entry
-    ZForwardingTableEntry entry = at(cursor);
-    while (!entry.is_empty()) {
-      if (entry.from_index() == from_index) {
-        // Match found, return already inserted address
-        return entry.to_offset();
-      }
-
-      entry = next(cursor);
-    }
-  }
+inline ZForwarding* ZForwardingTable::get(uintptr_t addr) const {
+  return _map.get(addr);
 }
 
 #endif // SHARE_GC_Z_ZFORWARDINGTABLE_INLINE_HPP
--- a/src/hotspot/share/gc/z/zForwardingTableEntry.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +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.
- */
-
-#ifndef SHARE_GC_Z_ZFORWARDINGTABLEENTRY_HPP
-#define SHARE_GC_Z_ZFORWARDINGTABLEENTRY_HPP
-
-#include "gc/z/zBitField.hpp"
-#include "memory/allocation.hpp"
-#include "metaprogramming/primitiveConversions.hpp"
-
-//
-// Forwarding table entry layout
-// -----------------------------
-//
-//   6                      4 4                                             0
-//   3                      2 1                                             0
-//  +------------------------+-----------------------------------------------+
-//  |11111111 11111111 111111|11 11111111 11111111 11111111 11111111 11111111|
-//  +------------------------+-----------------------------------------------+
-//  |                        |
-//  |                        * 41-0 To Object Offset (42-bits)
-//  |
-//  * 63-42 From Object Index (22-bits)
-//
-
-class ZForwardingTableEntry {
-  friend struct PrimitiveConversions;
-
-private:
-  typedef ZBitField<uint64_t, size_t, 0,  42> field_to_offset;
-  typedef ZBitField<uint64_t, size_t, 42, 22> field_from_index;
-
-  uint64_t _entry;
-
-  static uintptr_t empty() {
-    return (uintptr_t)-1;
-  }
-
-public:
-  ZForwardingTableEntry() :
-      _entry(empty()) {}
-
-  ZForwardingTableEntry(size_t from_index, size_t to_offset) :
-      _entry(field_from_index::encode(from_index) |
-             field_to_offset::encode(to_offset)) {}
-
-  bool is_empty() const {
-    return _entry == empty();
-  }
-
-  size_t to_offset() const {
-    return field_to_offset::decode(_entry);
-  }
-
-  size_t from_index() const {
-    return field_from_index::decode(_entry);
-  }
-};
-
-// Needed to allow atomic operations on ZForwardingTableEntry
-template <>
-struct PrimitiveConversions::Translate<ZForwardingTableEntry> : public TrueType {
-  typedef ZForwardingTableEntry Value;
-  typedef uint64_t              Decayed;
-
-  static Decayed decay(Value v) {
-    return v._entry;
-  }
-
-  static Value recover(Decayed d) {
-    ZForwardingTableEntry entry;
-    entry._entry = d;
-    return entry;
-  }
-};
-
-#endif // SHARE_GC_Z_ZFORWARDINGTABLEENTRY_HPP
--- a/src/hotspot/share/gc/z/zGranuleMap.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zGranuleMap.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -46,6 +46,7 @@
 
   T get(uintptr_t addr) const;
   void put(uintptr_t addr, T value);
+  void put(uintptr_t addr, size_t size, T value);
 };
 
 template <typename T>
@@ -58,6 +59,7 @@
   ZGranuleMapIterator(const ZGranuleMap<T>* map);
 
   bool next(T* value);
+  bool next(T** value);
 };
 
 #endif // SHARE_GC_Z_ZGRANULEMAP_HPP
--- a/src/hotspot/share/gc/z/zGranuleMap.inline.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zGranuleMap.inline.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -66,6 +66,17 @@
 }
 
 template <typename T>
+inline void ZGranuleMap<T>::put(uintptr_t addr, size_t size, T value) {
+  assert(is_aligned(size, ZGranuleSize), "Misaligned");
+
+  const size_t start_index = index_for_addr(addr);
+  const size_t end_index = start_index + (size >> ZGranuleSizeShift);
+  for (size_t index = start_index; index < end_index; index++) {
+    _map[index] = value;
+  }
+}
+
+template <typename T>
 inline ZGranuleMapIterator<T>::ZGranuleMapIterator(const ZGranuleMap<T>* map) :
     _map(map),
     _next(0) {}
@@ -81,4 +92,15 @@
   return false;
 }
 
+template <typename T>
+inline bool ZGranuleMapIterator<T>::next(T** value) {
+  if (_next < _map->size()) {
+    *value = _map->_map + _next++;
+    return true;
+  }
+
+  // End of map
+  return false;
+}
+
 #endif // SHARE_GC_Z_ZGRANULEMAP_INLINE_HPP
--- a/src/hotspot/share/gc/z/zHeap.cpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zHeap.cpp	Mon Mar 18 11:50:39 2019 +0100
@@ -64,6 +64,7 @@
     _object_allocator(_workers.nworkers()),
     _page_allocator(heap_min_size(), heap_max_size(), heap_max_reserve_size()),
     _pagetable(),
+    _forwarding_table(),
     _mark(&_workers, &_pagetable),
     _reference_processor(&_workers),
     _weak_roots_processor(&_workers),
@@ -234,17 +235,11 @@
   log_trace(gc)("Undo page allocation, thread: " PTR_FORMAT " (%s), page: " PTR_FORMAT ", size: " SIZE_FORMAT,
                 ZThread::id(), ZThread::name(), p2i(page), page->size());
 
-  release_page(page, false /* reclaimed */);
+  free_page(page, false /* reclaimed */);
 }
 
-bool ZHeap::retain_page(ZPage* page) {
-  return page->inc_refcount();
-}
-
-void ZHeap::release_page(ZPage* page, bool reclaimed) {
-  if (page->dec_refcount()) {
-    _page_allocator.free_page(page, reclaimed);
-  }
+void ZHeap::free_page(ZPage* page, bool reclaimed) {
+  _page_allocator.free_page(page, reclaimed);
 }
 
 void ZHeap::before_flip() {
@@ -437,7 +432,7 @@
       selector.register_garbage_page(page);
 
       // Reclaim page immediately
-      release_page(page, true /* reclaimed */);
+      free_page(page, true /* reclaimed */);
     }
   }
 
@@ -454,23 +449,17 @@
 void ZHeap::prepare_relocation_set() {
   ZRelocationSetIterator iter(&_relocation_set);
   for (ZPage* page; iter.next(&page);) {
-    // Prepare for relocation
-    page->set_forwarding();
-
-    // Update pagetable
-    _pagetable.set_relocating(page);
+    // Setup forwarding for page
+    _forwarding_table.insert(page->start(),
+                             page->size(),
+                             page->object_alignment_shift(),
+                             page->live_objects());
   }
 }
 
 void ZHeap::reset_relocation_set() {
-  ZRelocationSetIterator iter(&_relocation_set);
-  for (ZPage* page; iter.next(&page);) {
-    // Reset relocation information
-    page->reset_forwarding();
-
-    // Update pagetable
-    _pagetable.clear_relocating(page);
-  }
+  // Clear forwarding table
+  _forwarding_table.clear();
 }
 
 void ZHeap::relocate_start() {
@@ -493,25 +482,6 @@
   _relocate.start();
 }
 
-uintptr_t ZHeap::relocate_object(uintptr_t addr) {
-  assert(ZGlobalPhase == ZPhaseRelocate, "Relocate not allowed");
-  ZPage* const page = _pagetable.get(addr);
-  const bool retained = retain_page(page);
-  const uintptr_t new_addr = _relocate.relocate_object(page, addr);
-  if (retained) {
-    release_page(page, true /* reclaimed */);
-  }
-
-  return new_addr;
-}
-
-uintptr_t ZHeap::forward_object(uintptr_t addr) {
-  assert(ZGlobalPhase == ZPhaseMark ||
-         ZGlobalPhase == ZPhaseMarkCompleted, "Forward not allowed");
-  ZPage* const page = _pagetable.get(addr);
-  return _relocate.forward_object(page, addr);
-}
-
 void ZHeap::relocate() {
   // Relocate relocation set
   const bool success = _relocate.relocate(&_relocation_set);
--- a/src/hotspot/share/gc/z/zHeap.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zHeap.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -27,6 +27,7 @@
 #include "gc/shared/gcTimer.hpp"
 #include "gc/z/zAllocationFlags.hpp"
 #include "gc/z/zArray.hpp"
+#include "gc/z/zForwardingTable.hpp"
 #include "gc/z/zList.hpp"
 #include "gc/z/zLock.hpp"
 #include "gc/z/zMark.hpp"
@@ -55,6 +56,7 @@
   ZObjectAllocator    _object_allocator;
   ZPageAllocator      _page_allocator;
   ZPageTable          _pagetable;
+  ZForwardingTable    _forwarding_table;
   ZMark               _mark;
   ZReferenceProcessor _reference_processor;
   ZWeakRootsProcessor _weak_roots_processor;
@@ -123,8 +125,7 @@
   // Page allocation
   ZPage* alloc_page(uint8_t type, size_t size, ZAllocationFlags flags);
   void undo_alloc_page(ZPage* page);
-  bool retain_page(ZPage* page);
-  void release_page(ZPage* page, bool reclaimed);
+  void free_page(ZPage* page, bool reclaimed);
 
   // Object allocation
   uintptr_t alloc_tlab(size_t size);
@@ -152,10 +153,10 @@
   void reset_relocation_set();
 
   // Relocation
-  bool is_relocating(uintptr_t addr) const;
+  ZForwarding* forwarding(uintptr_t addr);
   void relocate_start();
   uintptr_t relocate_object(uintptr_t addr);
-  uintptr_t forward_object(uintptr_t addr);
+  uintptr_t remap_object(uintptr_t addr);
   void relocate();
 
   // Iteration
--- a/src/hotspot/share/gc/z/zHeap.inline.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zHeap.inline.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -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
@@ -25,6 +25,8 @@
 #define SHARE_GC_Z_ZHEAP_INLINE_HPP
 
 #include "gc/z/zAddress.inline.hpp"
+#include "gc/z/zForwarding.inline.hpp"
+#include "gc/z/zForwardingTable.inline.hpp"
 #include "gc/z/zHeap.hpp"
 #include "gc/z/zMark.inline.hpp"
 #include "gc/z/zOop.inline.hpp"
@@ -42,8 +44,8 @@
   return &_reference_processor;
 }
 
-inline bool ZHeap::is_relocating(uintptr_t addr) const {
-  return _pagetable.is_relocating(addr);
+inline ZForwarding* ZHeap::forwarding(uintptr_t addr) {
+  return _forwarding_table.get(addr);
 }
 
 inline bool ZHeap::is_object_live(uintptr_t addr) const {
@@ -89,6 +91,40 @@
   _object_allocator.undo_alloc_object_for_relocation(page, addr, size);
 }
 
+inline uintptr_t ZHeap::relocate_object(uintptr_t addr) {
+  assert(ZGlobalPhase == ZPhaseRelocate, "Relocate not allowed");
+
+  ZForwarding* const forwarding = _forwarding_table.get(addr);
+  if (forwarding == NULL) {
+    // Not forwarding
+    return ZAddress::good(addr);
+  }
+
+  // Relocate object
+  const bool retained = forwarding->inc_refcount();
+  const uintptr_t new_addr = _relocate.relocate_object(forwarding, addr);
+  if (retained && forwarding->dec_refcount()) {
+    ZPage* const page = _pagetable.get(addr);
+    free_page(page, true /* reclaimed */);
+  }
+
+  return new_addr;
+}
+
+inline uintptr_t ZHeap::remap_object(uintptr_t addr) {
+  assert(ZGlobalPhase == ZPhaseMark ||
+         ZGlobalPhase == ZPhaseMarkCompleted, "Forward not allowed");
+
+  ZForwarding* const forwarding = _forwarding_table.get(addr);
+  if (forwarding == NULL) {
+    // Not forwarding
+    return ZAddress::good(addr);
+  }
+
+  // Forward object
+  return _relocate.forward_object(forwarding, addr);
+}
+
 inline bool ZHeap::is_alloc_stalled() const {
   return _page_allocator.is_alloc_stalled();
 }
--- a/src/hotspot/share/gc/z/zPage.cpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zPage.cpp	Mon Mar 18 11:50:39 2019 +0100
@@ -31,14 +31,12 @@
 
 ZPage::ZPage(uint8_t type, ZVirtualMemory vmem, ZPhysicalMemory pmem) :
     _type(type),
-    _pinned(0),
+    _active(0),
     _numa_id((uint8_t)-1),
     _seqnum(0),
     _virtual(vmem),
     _top(start()),
     _livemap(object_max_count()),
-    _refcount(0),
-    _forwarding(),
     _physical(pmem) {
   assert(!_physical.is_null(), "Should not be null");
   assert(!_virtual.is_null(), "Should not be null");
@@ -55,7 +53,6 @@
 
 void ZPage::reset() {
   assert(!is_active(), "Should not be active");
-  assert(!is_pinned(), "Should not be pinned");
   assert(!is_detached(), "Should not be detached");
 
   _seqnum = ZGlobalSeqNum;
@@ -66,16 +63,14 @@
   // the reset of the above fields are visible.
   OrderAccess::storestore();
 
-  _refcount = 1;
+  _active = 1;
 }
 
 void ZPage::print_on(outputStream* out) const {
-  out->print_cr(" %-6s  " PTR_FORMAT " " PTR_FORMAT " " PTR_FORMAT " %s%s%s%s%s%s",
+  out->print_cr(" %-6s  " PTR_FORMAT " " PTR_FORMAT " " PTR_FORMAT " %s%s%s%s",
                 type_to_string(), start(), top(), end(),
                 is_allocating()  ? " Allocating"  : "",
                 is_relocatable() ? " Relocatable" : "",
-                is_forwarding()  ? " Forwarding"  : "",
-                is_pinned()      ? " Pinned"      : "",
                 is_detached()    ? " Detached"    : "",
                 !is_active()     ? " Inactive"    : "");
 }
--- a/src/hotspot/share/gc/z/zPage.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zPage.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -24,7 +24,6 @@
 #ifndef SHARE_GC_Z_ZPAGE_HPP
 #define SHARE_GC_Z_ZPAGE_HPP
 
-#include "gc/z/zForwardingTable.hpp"
 #include "gc/z/zList.hpp"
 #include "gc/z/zLiveMap.hpp"
 #include "gc/z/zPhysicalMemory.hpp"
@@ -38,7 +37,7 @@
 private:
   // Always hot
   const uint8_t        _type;             // Page type
-  volatile uint8_t     _pinned;           // Pinned flag
+  volatile uint8_t     _active;           // Active flag
   uint8_t              _numa_id;          // NUMA node affinity
   uint32_t             _seqnum;           // Allocation sequence number
   const ZVirtualMemory _virtual;          // Virtual start/end address
@@ -46,13 +45,10 @@
   ZLiveMap             _livemap;          // Live map
 
   // Hot when relocated and cached
-  volatile uint32_t    _refcount;         // Page reference count
-  ZForwardingTable     _forwarding;       // Forwarding table
   ZPhysicalMemory      _physical;         // Physical memory for page
   ZListNode<ZPage>     _node;             // Page list node
 
   const char* type_to_string() const;
-  uint32_t object_max_count() const;
 
   bool is_object_marked(uintptr_t addr) const;
   bool is_object_strongly_marked(uintptr_t addr) const;
@@ -61,6 +57,7 @@
   ZPage(uint8_t type, ZVirtualMemory vmem, ZPhysicalMemory pmem);
   ~ZPage();
 
+  uint32_t object_max_count() const;
   size_t object_alignment_shift() const;
   size_t object_alignment() const;
 
@@ -78,15 +75,14 @@
 
   void reset();
 
-  bool inc_refcount();
-  bool dec_refcount();
-
   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_active() const;
+  void set_inactive();
+
   bool is_allocating() const;
   bool is_relocatable() const;
   bool is_detached() const;
@@ -94,23 +90,13 @@
   bool is_mapped() const;
   void set_pre_mapped();
 
-  bool is_pinned() const;
-  void set_pinned();
-
-  bool is_forwarding() const;
-  void set_forwarding();
-  void reset_forwarding();
-  ZForwardingTableEntry find_forwarding(uintptr_t from_index);
-  ZForwardingTableEntry find_forwarding(uintptr_t from_index, ZForwardingTableCursor* cursor);
-  uintptr_t insert_forwarding(uintptr_t from_index, uintptr_t to_offset, ZForwardingTableCursor* cursor);
-  void verify_forwarding() const;
-
   bool is_marked() const;
   bool is_object_live(uintptr_t addr) const;
   bool is_object_strongly_live(uintptr_t addr) const;
   bool mark_object(uintptr_t addr, bool finalizable, bool& inc_live);
 
   void inc_live_atomic(uint32_t objects, size_t bytes);
+  uint32_t live_objects() const;
   size_t live_bytes() const;
 
   void object_iterate(ObjectClosure* cl);
--- a/src/hotspot/share/gc/z/zPage.inline.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zPage.inline.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -25,7 +25,6 @@
 #define SHARE_GC_Z_ZPAGE_INLINE_HPP
 
 #include "gc/z/zAddress.inline.hpp"
-#include "gc/z/zForwardingTable.inline.hpp"
 #include "gc/z/zGlobals.hpp"
 #include "gc/z/zLiveMap.inline.hpp"
 #include "gc/z/zMark.hpp"
@@ -35,6 +34,7 @@
 #include "gc/z/zVirtualMemory.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/atomic.hpp"
+#include "runtime/orderAccess.hpp"
 #include "utilities/align.hpp"
 #include "utilities/debug.hpp"
 
@@ -132,20 +132,6 @@
   return _numa_id;
 }
 
-inline bool ZPage::inc_refcount() {
-  for (uint32_t prev_refcount = _refcount; prev_refcount > 0; prev_refcount = _refcount) {
-    if (Atomic::cmpxchg(prev_refcount + 1, &_refcount, prev_refcount) == prev_refcount) {
-      return true;
-    }
-  }
-  return false;
-}
-
-inline bool ZPage::dec_refcount() {
-  assert(is_active(), "Should be active");
-  return Atomic::sub(1u, &_refcount) == 0;
-}
-
 inline bool ZPage::is_in(uintptr_t addr) const {
   const uintptr_t offset = ZAddress::offset(addr);
   return offset >= start() && offset < top();
@@ -164,7 +150,11 @@
 }
 
 inline bool ZPage::is_active() const {
-  return _refcount > 0;
+  return OrderAccess::load_acquire(&_active) != 0;
+}
+
+inline void ZPage::set_inactive() {
+  OrderAccess::release_store(&_active, (uint8_t)0);
 }
 
 inline bool ZPage::is_allocating() const {
@@ -190,44 +180,6 @@
   _seqnum = 1;
 }
 
-inline bool ZPage::is_pinned() const {
-  return _pinned;
-}
-
-inline void ZPage::set_pinned() {
-  _pinned = 1;
-}
-
-inline bool ZPage::is_forwarding() const {
-  return !_forwarding.is_null();
-}
-
-inline void ZPage::set_forwarding() {
-  assert(is_marked(), "Should be marked");
-  _forwarding.setup(_livemap.live_objects());
-}
-
-inline void ZPage::reset_forwarding() {
-  _forwarding.reset();
-  _pinned = 0;
-}
-
-inline ZForwardingTableEntry ZPage::find_forwarding(uintptr_t from_index) {
-  return _forwarding.find(from_index);
-}
-
-inline ZForwardingTableEntry ZPage::find_forwarding(uintptr_t from_index, ZForwardingTableCursor* cursor) {
-  return _forwarding.find(from_index, cursor);
-}
-
-inline uintptr_t ZPage::insert_forwarding(uintptr_t from_index, uintptr_t to_offset, ZForwardingTableCursor* cursor) {
-  return _forwarding.insert(from_index, to_offset, cursor);
-}
-
-inline void ZPage::verify_forwarding() const {
-  _forwarding.verify(object_max_count(), _livemap.live_objects());
-}
-
 inline bool ZPage::is_marked() const {
   assert(is_relocatable(), "Invalid page state");
   return _livemap.is_marked();
@@ -265,6 +217,11 @@
   _livemap.inc_live_atomic(objects, bytes);
 }
 
+inline uint32_t ZPage::live_objects() const {
+  assert(is_marked(), "Should be marked");
+  return _livemap.live_objects();
+}
+
 inline size_t ZPage::live_bytes() const {
   assert(is_marked(), "Should be marked");
   return _livemap.live_bytes();
--- a/src/hotspot/share/gc/z/zPageAllocator.cpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zPageAllocator.cpp	Mon Mar 18 11:50:39 2019 +0100
@@ -469,6 +469,9 @@
   // Update used statistics
   decrease_used(page->size(), reclaimed);
 
+  // Make page inactive
+  page->set_inactive();
+
   // Cache page
   _cache.free_page(page);
 
--- a/src/hotspot/share/gc/z/zPageCache.cpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zPageCache.cpp	Mon Mar 18 11:50:39 2019 +0100
@@ -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
@@ -119,7 +119,6 @@
 
 void ZPageCache::free_page(ZPage* page) {
   assert(!page->is_active(), "Invalid page state");
-  assert(!page->is_pinned(), "Invalid page state");
   assert(!page->is_detached(), "Invalid page state");
 
   const uint8_t type = page->type();
--- a/src/hotspot/share/gc/z/zRelocate.cpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zRelocate.cpp	Mon Mar 18 11:50:39 2019 +0100
@@ -24,6 +24,7 @@
 #include "precompiled.hpp"
 #include "gc/z/zAddress.inline.hpp"
 #include "gc/z/zBarrier.inline.hpp"
+#include "gc/z/zForwarding.inline.hpp"
 #include "gc/z/zHeap.hpp"
 #include "gc/z/zOopClosures.inline.hpp"
 #include "gc/z/zPage.hpp"
@@ -82,11 +83,16 @@
   _workers->run_parallel(&task);
 }
 
-uintptr_t ZRelocate::relocate_object_inner(ZPage* page, uintptr_t from_index, uintptr_t from_offset) const {
-  ZForwardingTableCursor cursor;
+ZForwarding* ZRelocate::forwarding_for_page(ZPage* page) const {
+  const uintptr_t addr = ZAddress::good(page->start());
+  return ZHeap::heap()->forwarding(addr);
+}
 
-  // Lookup address in forwarding table
-  const ZForwardingTableEntry entry = page->find_forwarding(from_index, &cursor);
+uintptr_t ZRelocate::relocate_object_inner(ZForwarding* forwarding, uintptr_t from_index, uintptr_t from_offset) const {
+  ZForwardingCursor cursor;
+
+  // Lookup forwarding entry
+  const ZForwardingEntry entry = forwarding->find(from_index, &cursor);
   if (entry.from_index() == from_index) {
     // Already relocated, return new address
     return entry.to_offset();
@@ -94,9 +100,9 @@
 
   assert(ZHeap::heap()->is_object_live(ZAddress::good(from_offset)), "Should be live");
 
-  if (page->is_pinned()) {
+  if (forwarding->is_pinned()) {
     // In-place forward
-    return page->insert_forwarding(from_index, from_offset, &cursor);
+    return forwarding->insert(from_index, from_offset, &cursor);
   }
 
   // Allocate object
@@ -105,15 +111,15 @@
   const uintptr_t to_good = ZHeap::heap()->alloc_object_for_relocation(size);
   if (to_good == 0) {
     // Failed, in-place forward
-    return page->insert_forwarding(from_index, from_offset, &cursor);
+    return forwarding->insert(from_index, from_offset, &cursor);
   }
 
   // Copy object
   ZUtils::object_copy(from_good, to_good, size);
 
-  // Update forwarding table
+  // Insert forwarding entry
   const uintptr_t to_offset = ZAddress::offset(to_good);
-  const uintptr_t to_offset_final = page->insert_forwarding(from_index, to_offset, &cursor);
+  const uintptr_t to_offset_final = forwarding->insert(from_index, to_offset, &cursor);
   if (to_offset_final == to_offset) {
     // Relocation succeeded
     return to_offset;
@@ -121,9 +127,9 @@
 
   // Relocation contention
   ZStatInc(ZCounterRelocationContention);
-  log_trace(gc)("Relocation contention, thread: " PTR_FORMAT " (%s), page: " PTR_FORMAT
-                ", entry: " SIZE_FORMAT ", oop: " PTR_FORMAT ", size: " SIZE_FORMAT,
-                ZThread::id(), ZThread::name(), p2i(this), cursor, from_good, size);
+  log_trace(gc)("Relocation contention, thread: " PTR_FORMAT " (%s), forwarding: " PTR_FORMAT
+                ", entry: " UINT32_FORMAT ", oop: " PTR_FORMAT ", size: " SIZE_FORMAT,
+                ZThread::id(), ZThread::name(), p2i(forwarding), cursor, from_good, size);
 
   // Try undo allocation
   ZHeap::heap()->undo_alloc_object_for_relocation(to_good, size);
@@ -131,44 +137,40 @@
   return to_offset_final;
 }
 
-uintptr_t ZRelocate::relocate_object(ZPage* page, uintptr_t from_addr) const {
-  assert(ZHeap::heap()->is_relocating(from_addr), "Should be relocating");
+uintptr_t ZRelocate::relocate_object(ZForwarding* forwarding, uintptr_t from_addr) const {
+  const uintptr_t from_offset = ZAddress::offset(from_addr);
+  const uintptr_t from_index = (from_offset - forwarding->start()) >> forwarding->object_alignment_shift();
+  const uintptr_t to_offset = relocate_object_inner(forwarding, from_index, from_offset);
 
-  const uintptr_t from_offset = ZAddress::offset(from_addr);
-  const uintptr_t from_index = (from_offset - page->start()) >> page->object_alignment_shift();
-  const uintptr_t to_offset = relocate_object_inner(page, from_index, from_offset);
   if (from_offset == to_offset) {
     // In-place forwarding, pin page
-    page->set_pinned();
+    forwarding->set_pinned();
   }
 
   return ZAddress::good(to_offset);
 }
 
-uintptr_t ZRelocate::forward_object(ZPage* page, uintptr_t from_addr) const {
-  assert(ZHeap::heap()->is_relocating(from_addr), "Should be relocated");
-
-  // Lookup address in forwarding table
+uintptr_t ZRelocate::forward_object(ZForwarding* forwarding, uintptr_t from_addr) const {
   const uintptr_t from_offset = ZAddress::offset(from_addr);
-  const uintptr_t from_index = (from_offset - page->start()) >> page->object_alignment_shift();
-  const ZForwardingTableEntry entry = page->find_forwarding(from_index);
+  const uintptr_t from_index = (from_offset - forwarding->start()) >> forwarding->object_alignment_shift();
+  const ZForwardingEntry entry = forwarding->find(from_index);
+
   assert(entry.from_index() == from_index, "Should be forwarded");
-
   return ZAddress::good(entry.to_offset());
 }
 
 class ZRelocateObjectClosure : public ObjectClosure {
 private:
-  ZRelocate* const _relocate;
-  ZPage* const     _page;
+  ZRelocate* const   _relocate;
+  ZForwarding* const _forwarding;
 
 public:
-  ZRelocateObjectClosure(ZRelocate* relocate, ZPage* page) :
+  ZRelocateObjectClosure(ZRelocate* relocate, ZForwarding* forwarding) :
       _relocate(relocate),
-      _page(page) {}
+      _forwarding(forwarding) {}
 
   virtual void do_object(oop o) {
-    _relocate->relocate_object(_page, ZOop::to_address(o));
+    _relocate->relocate_object(_forwarding, ZOop::to_address(o));
   }
 };
 
@@ -178,19 +180,22 @@
   // Relocate pages in the relocation set
   for (ZPage* page; iter->next(&page);) {
     // Relocate objects in page
-    ZRelocateObjectClosure cl(this, page);
+    ZForwarding* const forwarding = forwarding_for_page(page);
+    ZRelocateObjectClosure cl(this, forwarding);
     page->object_iterate(&cl);
 
     if (ZVerifyForwarding) {
-      page->verify_forwarding();
+      forwarding->verify(page->object_max_count(), page->live_objects());
     }
 
-    if (page->is_pinned()) {
+    if (forwarding->is_pinned()) {
       // Relocation failed, page is now pinned
       success = false;
     } else {
       // Relocation succeeded, release page
-      ZHeap::heap()->release_page(page, true /* reclaimed */);
+      if (forwarding->dec_refcount()) {
+        ZHeap::heap()->free_page(page, true /* reclaimed */);
+      }
     }
   }
 
--- a/src/hotspot/share/gc/z/zRelocate.hpp	Mon Mar 18 11:50:39 2019 +0100
+++ b/src/hotspot/share/gc/z/zRelocate.hpp	Mon Mar 18 11:50:39 2019 +0100
@@ -28,20 +28,23 @@
 #include "gc/z/zWorkers.hpp"
 #include "memory/allocation.hpp"
 
+class ZForwarding;
+
 class ZRelocate {
   friend class ZRelocateTask;
 
 private:
   ZWorkers* const _workers;
 
-  uintptr_t relocate_object_inner(ZPage* from_page, uintptr_t from_index, uintptr_t from_offset) const;
+  ZForwarding* forwarding_for_page(ZPage* page) const;
+  uintptr_t relocate_object_inner(ZForwarding* forwarding, uintptr_t from_index, uintptr_t from_offset) const;
   bool work(ZRelocationSetParallelIterator* iter);
 
 public:
   ZRelocate(ZWorkers* workers);
 
-  uintptr_t relocate_object(ZPage* from_page, uintptr_t from_addr) const;
-  uintptr_t forward_object(ZPage* from_page, uintptr_t from_addr) const;
+  uintptr_t relocate_object(ZForwarding* forwarding, uintptr_t from_addr) const;
+  uintptr_t forward_object(ZForwarding* forwarding, uintptr_t from_addr) const;
 
   void start();
   bool relocate(ZRelocationSet* relocation_set);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/gtest/gc/z/test_zForwarding.cpp	Mon Mar 18 11:50:39 2019 +0100
@@ -0,0 +1,184 @@
+/*
+ * 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
+ * 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/zForwarding.inline.hpp"
+#include "unittest.hpp"
+
+using namespace testing;
+
+#define CAPTURE_DELIM "\n"
+#define CAPTURE1(expression) #expression << " evaluates to " << expression
+#define CAPTURE2(e0, e1)                 CAPTURE1(e0) << CAPTURE_DELIM << CAPTURE1(e1)
+
+#define CAPTURE(expression) CAPTURE1(expression)
+
+class ZForwardingTest : public Test {
+public:
+  // Helper functions
+
+  static bool is_power_of_2(uint32_t value) {
+    return ::is_power_of_2((intptr_t)value);
+  }
+
+  class SequenceToFromIndex : AllStatic {
+  public:
+    static uintptr_t even(uint32_t sequence_number) {
+      return sequence_number * 2;
+    }
+    static uintptr_t odd(uint32_t sequence_number) {
+      return even(sequence_number) + 1;
+    }
+    static uintptr_t one_to_one(uint32_t sequence_number) {
+      return sequence_number;
+    }
+  };
+
+  // Test functions
+
+  static void setup(ZForwarding* forwarding) {
+    EXPECT_PRED1(is_power_of_2, forwarding->_nentries) << CAPTURE(forwarding->_nentries);
+  }
+
+  static void find_empty(ZForwarding* forwarding) {
+    uint32_t size = forwarding->_nentries;
+    uint32_t entries_to_check = size * 2;
+
+    for (uint32_t i = 0; i < entries_to_check; i++) {
+      uintptr_t from_index = SequenceToFromIndex::one_to_one(i);
+
+      EXPECT_TRUE(forwarding->find(from_index).is_empty()) << CAPTURE2(from_index, size);
+    }
+
+    EXPECT_TRUE(forwarding->find(uintptr_t(-1)).is_empty()) << CAPTURE(size);
+  }
+
+  static void find_full(ZForwarding* forwarding) {
+    uint32_t size = forwarding->_nentries;
+    uint32_t entries_to_populate = size;
+
+    // Populate
+    for (uint32_t i = 0; i < entries_to_populate; i++) {
+      uintptr_t from_index = SequenceToFromIndex::one_to_one(i);
+
+      ZForwardingCursor cursor;
+      ZForwardingEntry entry = forwarding->find(from_index, &cursor);
+      ASSERT_TRUE(entry.is_empty()) << CAPTURE2(from_index, size);
+
+      forwarding->insert(from_index, from_index, &cursor);
+    }
+
+    // Verify
+    for (uint32_t i = 0; i < entries_to_populate; i++) {
+      uintptr_t from_index = SequenceToFromIndex::one_to_one(i);
+
+      ZForwardingEntry entry = forwarding->find(from_index);
+      ASSERT_FALSE(entry.is_empty()) << CAPTURE2(from_index, size);
+
+      ASSERT_EQ(entry.from_index(), from_index) << CAPTURE(size);
+      ASSERT_EQ(entry.to_offset(), from_index) << CAPTURE(size);
+    }
+  }
+
+  static void find_every_other(ZForwarding* forwarding) {
+    uint32_t size = forwarding->_nentries;
+    uint32_t entries_to_populate = size / 2;
+
+    // Populate even from indices
+    for (uint32_t i = 0; i < entries_to_populate; i++) {
+      uintptr_t from_index = SequenceToFromIndex::even(i);
+
+      ZForwardingCursor cursor;
+      ZForwardingEntry entry = forwarding->find(from_index, &cursor);
+      ASSERT_TRUE(entry.is_empty()) << CAPTURE2(from_index, size);
+
+      forwarding->insert(from_index, from_index, &cursor);
+    }
+
+    // Verify populated even indices
+    for (uint32_t i = 0; i < entries_to_populate; i++) {
+      uintptr_t from_index = SequenceToFromIndex::even(i);
+
+      ZForwardingCursor cursor;
+      ZForwardingEntry entry = forwarding->find(from_index, &cursor);
+      ASSERT_FALSE(entry.is_empty()) << CAPTURE2(from_index, size);
+
+      ASSERT_EQ(entry.from_index(), from_index) << CAPTURE(size);
+      ASSERT_EQ(entry.to_offset(), from_index) << CAPTURE(size);
+    }
+
+    // Verify empty odd indices
+    //
+    // This check could be done on a larger range of sequence numbers,
+    // but currently entries_to_populate is used.
+    for (uint32_t i = 0; i < entries_to_populate; i++) {
+      uintptr_t from_index = SequenceToFromIndex::odd(i);
+
+      ZForwardingEntry entry = forwarding->find(from_index);
+
+      ASSERT_TRUE(entry.is_empty()) << CAPTURE2(from_index, size);
+    }
+  }
+
+  static void test(void (*function)(ZForwarding*), uint32_t size) {
+    // Setup
+    ZForwarding* forwarding = ZForwarding::create(0 /* start */,
+                                                  0 /* object_alignment_shift */,
+                                                  size);
+
+    // Actual test function
+    (*function)(forwarding);
+
+    // Teardown
+    ZForwarding::destroy(forwarding);
+  }
+
+  // Run the given function with a few different input values.
+  static void test(void (*function)(ZForwarding*)) {
+    test(function, 1);
+    test(function, 2);
+    test(function, 3);
+    test(function, 4);
+    test(function, 7);
+    test(function, 8);
+    test(function, 1023);
+    test(function, 1024);
+    test(function, 1025);
+  }
+};
+
+TEST_F(ZForwardingTest, setup) {
+  test(&ZForwardingTest::setup);
+}
+
+TEST_F(ZForwardingTest, find_empty) {
+  test(&ZForwardingTest::find_empty);
+}
+
+TEST_F(ZForwardingTest, find_full) {
+  test(&ZForwardingTest::find_full);
+}
+
+TEST_F(ZForwardingTest, find_every_other) {
+  test(&ZForwardingTest::find_every_other);
+}
--- a/test/hotspot/gtest/gc/z/test_zForwardingTable.cpp	Mon Mar 18 11:50:39 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,185 +0,0 @@
-/*
- * Copyright (c) 2016, 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/zForwardingTable.inline.hpp"
-#include "unittest.hpp"
-
-using namespace testing;
-
-#define CAPTURE_DELIM "\n"
-#define CAPTURE1(expression) #expression << " evaluates to " << expression
-#define CAPTURE2(e0, e1)                 CAPTURE1(e0) << CAPTURE_DELIM << CAPTURE1(e1)
-
-#define CAPTURE(expression) CAPTURE1(expression)
-
-class ZForwardingTableTest : public Test {
-public:
-  // Helper functions
-
-  static bool is_power_of_2(size_t value) {
-    return ::is_power_of_2((intptr_t)value);
-  }
-
-  class SequenceToFromIndex : AllStatic {
-  public:
-    static uintptr_t even(uint32_t sequence_number) {
-      return sequence_number * 2;
-    }
-    static uintptr_t odd(uint32_t sequence_number) {
-      return even(sequence_number) + 1;
-    }
-    static uintptr_t one_to_one(uint32_t sequence_number) {
-      return sequence_number;
-    }
-  };
-
-  // Test functions
-
-  static void setup(ZForwardingTable& table) {
-    EXPECT_PRED1(is_power_of_2, table._size) << CAPTURE(table._size);
-  }
-
-  static void find_empty(ZForwardingTable& table) {
-    size_t size = table._size;
-    size_t entries_to_check = size * 2;
-
-    for (uint32_t i = 0; i < entries_to_check; i++) {
-      uintptr_t from_index = SequenceToFromIndex::one_to_one(i);
-
-      EXPECT_TRUE(table.find(from_index).is_empty()) << CAPTURE2(from_index, size);
-    }
-
-    EXPECT_TRUE(table.find(uintptr_t(-1)).is_empty()) << CAPTURE(size);
-  }
-
-  static void find_full(ZForwardingTable& table) {
-    size_t size = table._size;
-    size_t entries_to_populate = size;
-
-    // Populate
-    for (uint32_t i = 0; i < entries_to_populate; i++) {
-      uintptr_t from_index = SequenceToFromIndex::one_to_one(i);
-
-      ZForwardingTableCursor cursor;
-      ZForwardingTableEntry entry = table.find(from_index, &cursor);
-      ASSERT_TRUE(entry.is_empty()) << CAPTURE2(from_index, size);
-
-      table.insert(from_index, from_index, &cursor);
-    }
-
-    // Verify
-    for (uint32_t i = 0; i < entries_to_populate; i++) {
-      uintptr_t from_index = SequenceToFromIndex::one_to_one(i);
-
-      ZForwardingTableEntry entry = table.find(from_index);
-      ASSERT_FALSE(entry.is_empty()) << CAPTURE2(from_index, size);
-
-      ASSERT_EQ(entry.from_index(), from_index) << CAPTURE(size);
-      ASSERT_EQ(entry.to_offset(), from_index) << CAPTURE(size);
-    }
-  }
-
-  static void find_every_other(ZForwardingTable& table) {
-    size_t size = table._size;
-    size_t entries_to_populate = size / 2;
-
-    // Populate even from indices
-    for (uint32_t i = 0; i < entries_to_populate; i++) {
-      uintptr_t from_index = SequenceToFromIndex::even(i);
-
-      ZForwardingTableCursor cursor;
-      ZForwardingTableEntry entry = table.find(from_index, &cursor);
-      ASSERT_TRUE(entry.is_empty()) << CAPTURE2(from_index, size);
-
-      table.insert(from_index, from_index, &cursor);
-    }
-
-    // Verify populated even indices
-    for (uint32_t i = 0; i < entries_to_populate; i++) {
-      uintptr_t from_index = SequenceToFromIndex::even(i);
-
-      ZForwardingTableCursor cursor;
-      ZForwardingTableEntry entry = table.find(from_index, &cursor);
-      ASSERT_FALSE(entry.is_empty()) << CAPTURE2(from_index, size);
-
-      ASSERT_EQ(entry.from_index(), from_index) << CAPTURE(size);
-      ASSERT_EQ(entry.to_offset(), from_index) << CAPTURE(size);
-    }
-
-    // Verify empty odd indices
-    //
-    // This check could be done on a larger range of sequence numbers,
-    // but currently entries_to_populate is used.
-    for (uint32_t i = 0; i < entries_to_populate; i++) {
-      uintptr_t from_index = SequenceToFromIndex::odd(i);
-
-      ZForwardingTableEntry entry = table.find(from_index);
-
-      ASSERT_TRUE(entry.is_empty()) << CAPTURE2(from_index, size);
-    }
-  }
-
-  static void test(void (*function)(ZForwardingTable&), uint32_t size) {
-    // Setup
-    ZForwardingTable table;
-    table.setup(size);
-    ASSERT_FALSE(table.is_null());
-
-    // Actual test function
-    (*function)(table);
-
-    // Teardown
-    table.reset();
-    ASSERT_TRUE(table.is_null());
-  }
-
-  // Run the given function with a few different input values.
-  static void test(void (*function)(ZForwardingTable&)) {
-    test(function, 1);
-    test(function, 2);
-    test(function, 3);
-    test(function, 4);
-    test(function, 7);
-    test(function, 8);
-    test(function, 1023);
-    test(function, 1024);
-    test(function, 1025);
-  }
-};
-
-TEST_F(ZForwardingTableTest, setup) {
-  test(&ZForwardingTableTest::setup);
-}
-
-TEST_F(ZForwardingTableTest, find_empty) {
-  test(&ZForwardingTableTest::find_empty);
-}
-
-TEST_F(ZForwardingTableTest, find_full) {
-  test(&ZForwardingTableTest::find_full);
-}
-
-TEST_F(ZForwardingTableTest, find_every_other) {
-  test(&ZForwardingTableTest::find_every_other);
-}
--- a/test/hotspot/jtreg/ProblemList-zgc.txt	Mon Mar 18 11:50:39 2019 +0100
+++ b/test/hotspot/jtreg/ProblemList-zgc.txt	Mon Mar 18 11:50:39 2019 +0100
@@ -27,4 +27,16 @@
 #
 #############################################################################
 
+serviceability/sa/ClhsdbInspect.java                          8220624   generic-all
+serviceability/sa/ClhsdbJdis.java                             8220624   generic-all
+serviceability/sa/ClhsdbJhisto.java                           8220624   generic-all
+serviceability/sa/ClhsdbJstack.java                           8220624   generic-all
+serviceability/sa/ClhsdbPrintAs.java                          8220624   generic-all
+serviceability/sa/ClhsdbPstack.java                           8220624   generic-all
+serviceability/sa/ClhsdbSource.java                           8220624   generic-all
+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