diff a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp
--- a/src/hotspot/share/gc/z/zRelocate.cpp
+++ b/src/hotspot/share/gc/z/zRelocate.cpp
@@ -321,23 +321,26 @@
 
 static zaddress relocate_object_inner(ZForwarding* forwarding, zaddress from_addr, ZForwardingCursor* cursor) {
   assert(ZHeap::heap()->is_object_live(from_addr), "Should be live");
 
   // Allocate object
-  const size_t size = ZUtils::object_size(from_addr);
+  const size_t old_size = ZUtils::object_size(from_addr);
+  const size_t size = ZUtils::copy_size(from_addr, old_size);
 
   ZAllocatorForRelocation* allocator = ZAllocator::relocation(forwarding->to_age());
 
   const zaddress to_addr = allocator->alloc_object(size);
 
   if (is_null(to_addr)) {
     // Allocation failed
     return zaddress::null;
   }
+  assert(to_addr != from_addr, "addresses must be different");
 
   // Copy object
-  ZUtils::object_copy_disjoint(from_addr, to_addr, size);
+  ZUtils::object_copy_disjoint(from_addr, to_addr, old_size);
+  ZUtils::initialize_hash_if_necessary(to_addr, from_addr);
 
   // Insert forwarding
   const zaddress to_addr_final = forwarding->insert(from_addr, to_addr, cursor);
 
   if (to_addr_final != to_addr) {
@@ -585,15 +588,17 @@
     } else {
       _other_compacted += aligned_size;
     }
   }
 
-  zaddress try_relocate_object_inner(zaddress from_addr) {
+  zaddress try_relocate_object_inner(zaddress from_addr, size_t old_size) {
     ZForwardingCursor cursor;
-
-    const size_t size = ZUtils::object_size(from_addr);
     ZPage* const to_page = target(_forwarding->to_age());
+    zoffset_end from_offset = to_zoffset_end(ZAddress::offset(from_addr));
+    zoffset_end top = to_page != nullptr ? to_page->top() : to_zoffset_end(0);
+    const size_t new_size = ZUtils::copy_size(from_addr, old_size);
+    const size_t size = top == from_offset ? old_size : new_size;
 
     // Lookup forwarding
     {
       const zaddress to_addr = _forwarding->find(from_addr, &cursor);
       if (!is_null(to_addr)) {
@@ -607,17 +612,24 @@
     const zaddress allocated_addr = _allocator->alloc_object(to_page, size);
     if (is_null(allocated_addr)) {
       // Allocation failed
       return zaddress::null;
     }
+    if (old_size != new_size && ((top == from_offset) != (allocated_addr == from_addr))) {
+      _allocator->undo_alloc_object(to_page, allocated_addr, size);
+      return zaddress::null;
+    }
 
     // Copy object. Use conjoint copying if we are relocating
     // in-place and the new object overlaps with the old object.
-    if (_forwarding->in_place_relocation() && allocated_addr + size > from_addr) {
-      ZUtils::object_copy_conjoint(from_addr, allocated_addr, size);
+    if (_forwarding->in_place_relocation() && allocated_addr + old_size > from_addr) {
+      ZUtils::object_copy_conjoint(from_addr, allocated_addr, old_size);
     } else {
-      ZUtils::object_copy_disjoint(from_addr, allocated_addr, size);
+      ZUtils::object_copy_disjoint(from_addr, allocated_addr, old_size);
+    }
+    if (from_addr != allocated_addr) {
+      ZUtils::initialize_hash_if_necessary(allocated_addr, from_addr);
     }
 
     // Insert forwarding
     const zaddress to_addr = _forwarding->insert(from_addr, allocated_addr, &cursor);
     if (to_addr != allocated_addr) {
@@ -627,11 +639,11 @@
     }
 
     return to_addr;
   }
 
-  void update_remset_old_to_old(zaddress from_addr, zaddress to_addr) const {
+  void update_remset_old_to_old(zaddress from_addr, zaddress to_addr, size_t size) const {
     // Old-to-old relocation - move existing remset bits
 
     // If this is called for an in-place relocated page, then this code has the
     // responsibility to clear the old remset bits. Extra care is needed because:
     //
@@ -649,14 +661,12 @@
 
     // Uses _relaxed version to handle that in-place relocation resets _top
     assert(ZHeap::heap()->is_in_page_relaxed(from_page, from_addr), "Must be");
     assert(to_page->is_in(to_addr), "Must be");
 
-
-    // Read the size from the to-object, since the from-object
-    // could have been overwritten during in-place relocation.
-    const size_t size = ZUtils::object_size(to_addr);
+    assert(size <= ZUtils::object_size(to_addr), "old size must be <= new size");
+    assert(size > 0, "size must be set");
 
     // If a young generation collection started while the old generation
     // relocated  objects, the remember set bits were flipped from "current"
     // to "previous".
     //
@@ -777,34 +787,35 @@
 
   void update_remset_promoted(zaddress to_addr) const {
     ZIterator::basic_oop_iterate(to_oop(to_addr), update_remset_promoted_filter_and_remap_per_field);
   }
 
-  void update_remset_for_fields(zaddress from_addr, zaddress to_addr) const {
+  void update_remset_for_fields(zaddress from_addr, zaddress to_addr, size_t size) const {
     if (_forwarding->to_age() != ZPageAge::old) {
       // No remembered set in young pages
       return;
     }
 
     // Need to deal with remset when moving objects to the old generation
     if (_forwarding->from_age() == ZPageAge::old) {
-      update_remset_old_to_old(from_addr, to_addr);
+      update_remset_old_to_old(from_addr, to_addr, size);
       return;
     }
 
     // Normal promotion
     update_remset_promoted(to_addr);
   }
 
   bool try_relocate_object(zaddress from_addr) {
-    const zaddress to_addr = try_relocate_object_inner(from_addr);
+    size_t size = ZUtils::object_size(from_addr);
+    const zaddress to_addr = try_relocate_object_inner(from_addr, size);
 
     if (is_null(to_addr)) {
       return false;
     }
 
-    update_remset_for_fields(from_addr, to_addr);
+    update_remset_for_fields(from_addr, to_addr, size);
 
     return true;
   }
 
   void start_in_place_relocation_prepare_remset(ZPage* from_page) {
