diff --git a/src/hotspot/share/gc/z/zGeneration.cpp b/src/hotspot/share/gc/z/zGeneration.cpp
index 3632d2ee4d9..f9e0904e1d8 100644
--- a/src/hotspot/share/gc/z/zGeneration.cpp
+++ b/src/hotspot/share/gc/z/zGeneration.cpp
@@ -371,6 +371,7 @@ void ZGeneration::at_collection_start(ConcurrentGCTimer* gc_timer) {
   set_gc_timer(gc_timer);
   stat_cycle()->at_start();
   stat_heap()->at_collection_start(_page_allocator->stats(this));
+  stat_relocation()->at_collection_start();
   workers()->set_active();
 }
 
diff --git a/src/hotspot/share/gc/z/zRelocationSetSelector.cpp b/src/hotspot/share/gc/z/zRelocationSetSelector.cpp
index 04f3bdbe5bc..b400a2cd6a2 100644
--- a/src/hotspot/share/gc/z/zRelocationSetSelector.cpp
+++ b/src/hotspot/share/gc/z/zRelocationSetSelector.cpp
@@ -41,6 +41,28 @@ ZRelocationSetSelectorGroupStats::ZRelocationSetSelectorGroupStats() :
     _npages_selected(0),
     _relocate(0) {}
 
+ZRelocationSetSelectorStats::ZRelocationSetSelectorStats() :
+    _has_relocatable_pages(false) {
+  for (uint i = 0; i <= ZPageAgeMax; ++i) {
+    _small[i] = ZRelocationSetSelectorGroupStats();
+    _medium[i] = ZRelocationSetSelectorGroupStats();
+    _large[i] = ZRelocationSetSelectorGroupStats();
+  }
+}
+
+ZRelocationSetSelectorStats::ZRelocationSetSelectorStats(const ZRelocationSetSelectorGroup* small,
+                                                         const ZRelocationSetSelectorGroup* medium,
+                                                         const ZRelocationSetSelectorGroup* large,
+                                                         bool has_relocatable_pages) :
+    _has_relocatable_pages(has_relocatable_pages) {
+  for (uint i = 0; i <= ZPageAgeMax; ++i) {
+    const ZPageAge age = static_cast<ZPageAge>(i);
+    _small[i] = small->stats(age);
+    _medium[i] = medium->stats(age);
+    _large[i] = large->stats(age);
+  }
+}
+
 ZRelocationSetSelectorGroup::ZRelocationSetSelectorGroup(const char* name,
                                                          ZPageType page_type,
                                                          size_t page_size,
@@ -235,16 +257,6 @@ void ZRelocationSetSelector::select() {
 }
 
 ZRelocationSetSelectorStats ZRelocationSetSelector::stats() const {
-  ZRelocationSetSelectorStats stats;
-
-  for (uint i = 0; i <= ZPageAgeMax; ++i) {
-    const ZPageAge age = static_cast<ZPageAge>(i);
-    stats._small[i] = _small.stats(age);
-    stats._medium[i] = _medium.stats(age);
-    stats._large[i] = _large.stats(age);
-  }
-
-  stats._has_relocatable_pages = total() > 0;
-
-  return stats;
+  const bool has_relocatable_pages = total() > 0;
+  return ZRelocationSetSelectorStats(&_small, &_medium, &_large, has_relocatable_pages);
 }
diff --git a/src/hotspot/share/gc/z/zRelocationSetSelector.hpp b/src/hotspot/share/gc/z/zRelocationSetSelector.hpp
index 1557097568c..b8e792e5105 100644
--- a/src/hotspot/share/gc/z/zRelocationSetSelector.hpp
+++ b/src/hotspot/share/gc/z/zRelocationSetSelector.hpp
@@ -31,6 +31,7 @@
 #include "memory/allocation.hpp"
 
 class ZPage;
+class ZRelocationSetSelectorGroup;
 
 class ZRelocationSetSelectorGroupStats {
   friend class ZRelocationSetSelectorGroup;
@@ -66,13 +67,22 @@ class ZRelocationSetSelectorStats {
   ZRelocationSetSelectorGroupStats _medium[ZPageAgeMax + 1];
   ZRelocationSetSelectorGroupStats _large[ZPageAgeMax + 1];
 
-  size_t _has_relocatable_pages;
+  bool _has_relocatable_pages;
 
 public:
+  ZRelocationSetSelectorStats();
+  ZRelocationSetSelectorStats(const ZRelocationSetSelectorGroup* small,
+                              const ZRelocationSetSelectorGroup* medium,
+                              const ZRelocationSetSelectorGroup* large,
+                              bool has_relocatable_pages);
+
   const ZRelocationSetSelectorGroupStats& small(ZPageAge age) const;
   const ZRelocationSetSelectorGroupStats& medium(ZPageAge age) const;
   const ZRelocationSetSelectorGroupStats& large(ZPageAge age) const;
 
+  size_t live_bytes(ZPageAge age) const;
+  size_t npages(ZPageAge age) const;
+
   bool has_relocatable_pages() const;
 };
 
diff --git a/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp b/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp
index 6f3a7130d6d..ecc3a646dd5 100644
--- a/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp
+++ b/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp
@@ -53,10 +53,6 @@ inline size_t ZRelocationSetSelectorGroupStats::relocate() const {
   return _relocate;
 }
 
-inline bool ZRelocationSetSelectorStats::has_relocatable_pages() const {
-  return _has_relocatable_pages;
-}
-
 inline const ZRelocationSetSelectorGroupStats& ZRelocationSetSelectorStats::small(ZPageAge age) const {
   return _small[static_cast<uint>(age)];
 }
@@ -69,6 +65,20 @@ inline const ZRelocationSetSelectorGroupStats& ZRelocationSetSelectorStats::larg
   return _large[static_cast<uint>(age)];
 }
 
+inline size_t ZRelocationSetSelectorStats::live_bytes(ZPageAge age) const {
+  return small(age).live() + medium(age).live() + large(age).live();
+}
+
+inline size_t ZRelocationSetSelectorStats::npages(ZPageAge age) const {
+  return small(age).npages_candidates() +
+         medium(age).npages_candidates() +
+         large(age).npages_candidates();
+}
+
+inline bool ZRelocationSetSelectorStats::has_relocatable_pages() const {
+  return _has_relocatable_pages;
+}
+
 inline void ZRelocationSetSelectorGroup::register_live_page(ZPage* page) {
   const size_t size = page->size();
   const size_t live = page->live_bytes();
diff --git a/src/hotspot/share/gc/z/zStat.cpp b/src/hotspot/share/gc/z/zStat.cpp
index b282b6e4154..f9f8f26ca9b 100644
--- a/src/hotspot/share/gc/z/zStat.cpp
+++ b/src/hotspot/share/gc/z/zStat.cpp
@@ -1470,6 +1470,11 @@ ZStatRelocation::ZStatRelocation() :
     _medium_in_place_count() {
 }
 
+void ZStatRelocation::at_collection_start() {
+  _previous_selector_stats = _selector_stats;
+  _selector_stats = {};
+}
+
 void ZStatRelocation::at_select_relocation_set(const ZRelocationSetSelectorStats& selector_stats) {
   _selector_stats = selector_stats;
 }
@@ -1483,7 +1488,7 @@ void ZStatRelocation::at_relocate_end(size_t small_in_place_count, size_t medium
   _medium_in_place_count = medium_in_place_count;
 }
 
-void ZStatRelocation::print_page_summary() {
+void ZStatRelocation::print_page_summary() const {
   LogTarget(Info, gc, reloc) lt;
 
   if (!_selector_stats.has_relocatable_pages() || !lt.is_enabled()) {
@@ -1491,6 +1496,15 @@ void ZStatRelocation::print_page_summary() {
     return;
   }
 
+  struct ZStatRelocationSummary {
+    size_t npages_candidates;
+    size_t total;
+    size_t live;
+    size_t empty;
+    size_t npages_selected;
+    size_t relocate;
+  };
+
   // Zero initialize
   ZStatRelocationSummary small_summary{};
   ZStatRelocationSummary medium_summary{};
@@ -1544,79 +1558,90 @@ void ZStatRelocation::print_page_summary() {
   lt.print("Forwarding Usage: " SIZE_FORMAT "M", _forwarding_usage / M);
 }
 
-void ZStatRelocation::print_age_table() {
+void ZStatRelocation::print_age_table() const {
   LogTarget(Info, gc, reloc) lt;
+
   if (!_selector_stats.has_relocatable_pages() || !lt.is_enabled()) {
     // Nothing to log or logging not enabled.
     return;
   }
 
+  // Find range of pages to print
+  const uint end = [&]() {
+    for (uint i = 0; i <= ZPageAgeMax; ++i) {
+      const ZPageAge age = static_cast<ZPageAge>(i);
+      if (age == ZPageAge::old) {
+        return i;
+      }
+
+      if (_selector_stats.npages(age) == 0 && _previous_selector_stats.npages(age) == 0) {
+        return i;
+      }
+    }
+
+    return ZPageAgeMax;
+  }();
+
   ZStatTablePrinter age_table(11, 18);
   lt.print("Age Table:");
   lt.print("%s", age_table()
            .fill()
            .center("Live")
-           .center("Garbage")
            .center("Small")
            .center("Medium")
            .center("Large")
            .end());
 
-  size_t live[ZPageAgeMax + 1] = {};
-  size_t total[ZPageAgeMax + 1] = {};
-
-  uint oldest_none_empty_age = 0;
+  auto unit = [](size_t bytes) {
+    if (bytes < K) {
+      return "B";
+    }
+    if (bytes < M) {
+      return "K";
+    }
+    return "M";
+  };
 
-  for (uint i = 0; i <= ZPageAgeMax; ++i) {
-    ZPageAge age = static_cast<ZPageAge>(i);
-    auto summarize_pages = [&](const ZRelocationSetSelectorGroupStats& stats) {
-      live[i] += stats.live();
-      total[i] += stats.total();
-    };
-
-    summarize_pages(_selector_stats.small(age));
-    summarize_pages(_selector_stats.medium(age));
-    summarize_pages(_selector_stats.large(age));
-
-    if (total[i] != 0) {
-      oldest_none_empty_age = i;
+  auto value = [](size_t bytes) {
+    if (bytes < K) {
+      return bytes;
     }
-  }
+    if (bytes < M) {
+      return bytes / K;
+    }
+    return bytes / M;
+  };
 
-  for (uint i = 0; i <= oldest_none_empty_age; ++i) {
-    ZPageAge age = static_cast<ZPageAge>(i);
+  for (uint i = 0; i < end; ++i) {
+    const ZPageAge age = static_cast<ZPageAge>(i);
 
-    FormatBuffer<> age_str("");
+    FormatBuffer<> desc("");
     if (age == ZPageAge::eden) {
-      age_str.append("Eden");
-    } else if (age != ZPageAge::old) {
-      age_str.append("Survivor %d", i);
+      desc.append("Eden");
+    } else {
+      desc.append("Survivor %d", i);
     }
 
-    auto create_age_table = [&]() {
-      if (live[i] == 0) {
-        return age_table()
-              .left("%s", age_str.buffer())
-              .left(ZTABLE_ARGS_NA);
-      } else {
-        return age_table()
-              .left("%s", age_str.buffer())
-              .left(ZTABLE_ARGS(live[i]));
-      }
-    };
-
-    lt.print("%s", create_age_table()
-              .left(ZTABLE_ARGS(total[i] - live[i]))
-              .left(SIZE_FORMAT_W(7) " / " SIZE_FORMAT,
-                    _selector_stats.small(age).npages_candidates(),
-                    _selector_stats.small(age).npages_selected())
-              .left(SIZE_FORMAT_W(7) " / " SIZE_FORMAT,
-                    _selector_stats.medium(age).npages_candidates(),
-                    _selector_stats.medium(age).npages_selected())
-              .left(SIZE_FORMAT_W(7) " / " SIZE_FORMAT,
-                    _selector_stats.large(age).npages_candidates(),
-                    _selector_stats.large(age).npages_selected())
-              .end());
+    const size_t prev_live_bytes = _previous_selector_stats.live_bytes(age);
+    const size_t live_bytes = _selector_stats.live_bytes(age);
+
+    lt.print("%s", age_table()
+             .left("%s", desc.buffer())
+             .left(SIZE_FORMAT_W(8) "%s" " -> " SIZE_FORMAT "%s",
+                   value(prev_live_bytes),
+                   unit(prev_live_bytes),
+                   value(live_bytes),
+                   unit(live_bytes))
+             .left(SIZE_FORMAT_W(7) " -> " SIZE_FORMAT,
+                   _previous_selector_stats.small(age).npages_candidates(),
+                   _selector_stats.small(age).npages_candidates())
+             .left(SIZE_FORMAT_W(7) " -> " SIZE_FORMAT,
+                   _previous_selector_stats.medium(age).npages_candidates(),
+                   _selector_stats.medium(age).npages_candidates())
+             .left(SIZE_FORMAT_W(7) " -> " SIZE_FORMAT,
+                   _previous_selector_stats.large(age).npages_candidates(),
+                   _selector_stats.large(age).npages_candidates())
+             .end());
   }
 }
 
diff --git a/src/hotspot/share/gc/z/zStat.hpp b/src/hotspot/share/gc/z/zStat.hpp
index 3bb719987c8..8fa6ddfae0f 100644
--- a/src/hotspot/share/gc/z/zStat.hpp
+++ b/src/hotspot/share/gc/z/zStat.hpp
@@ -510,40 +510,29 @@ class ZStatMark {
   void print();
 };
 
-struct ZStatRelocationSummary {
-  size_t npages_candidates;
-  size_t total;
-  size_t live;
-  size_t empty;
-  size_t npages_selected;
-  size_t relocate;
-};
-
 //
 // Stat relocation
 //
 class ZStatRelocation {
 private:
   ZRelocationSetSelectorStats _selector_stats;
+  ZRelocationSetSelectorStats _previous_selector_stats;
   size_t                      _forwarding_usage;
   size_t                      _small_selected;
   size_t                      _small_in_place_count;
   size_t                      _medium_selected;
   size_t                      _medium_in_place_count;
 
-  void print(const char* name,
-             ZStatRelocationSummary selector_group,
-             size_t in_place_count);
-
 public:
   ZStatRelocation();
 
+  void at_collection_start();
   void at_select_relocation_set(const ZRelocationSetSelectorStats& selector_stats);
   void at_install_relocation_set(size_t forwarding_usage);
   void at_relocate_end(size_t small_in_place_count, size_t medium_in_place_count);
 
-  void print_page_summary();
-  void print_age_table();
+  void print_page_summary() const;
+  void print_age_table() const;
 };
 
 //