diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index 33e9acab4a..f2ae497266 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -46,6 +46,7 @@
 #include "pgstat.h"
 #include "replication/slot.h"
 #include "storage/fd.h"
+#include "storage/ipc.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
 #include "utils/builtins.h"
@@ -101,6 +102,8 @@ int			max_replication_slots = 0;	/* the maximum number of replication
 
 static void ReplicationSlotDropAcquired(void);
 static void ReplicationSlotDropPtr(ReplicationSlot *slot);
+static void RegisterReplicationSlotCallback(void);
+static void ReplicationSlotBeforeShmemExit(int code, Datum arg);
 
 /* internal persistency functions */
 static void RestoreSlotFromDisk(const char *name);
@@ -301,6 +304,12 @@ ReplicationSlotCreate(const char *name, bool db_specific,
 	 */
 	CreateSlotOnDisk(slot);
 
+	/*
+	 * Register callback to make sure cleanup and releasing the replication
+	 * slot on exit.
+	 */
+	RegisterReplicationSlotCallback();
+
 	/*
 	 * We need to briefly prevent any other backend from iterating over the
 	 * slots while we flip the in_use flag. We also need to set the active
@@ -455,6 +464,9 @@ retry:
 	/* Let everybody know we've modified this slot */
 	ConditionVariableBroadcast(&s->active_cv);
 
+	/* Register callback to make sure releasing the replication slot on exit */
+	RegisterReplicationSlotCallback();
+
 	/* We made this slot active, so it's ours now. */
 	MyReplicationSlot = s;
 }
@@ -702,6 +714,45 @@ ReplicationSlotDropPtr(ReplicationSlot *slot)
 	LWLockRelease(ReplicationSlotAllocationLock);
 }
 
+/*
+ * Register the callback for replication slot cleanup and releasing if not
+ * registered yet.
+ */
+static void
+RegisterReplicationSlotCallback(void)
+{
+	static bool callback_registered = false;
+
+	if (callback_registered)
+		return;
+
+	/* The callback must be registered before acquiring a replication slot */
+	Assert(MyReplicationSlot == NULL);
+
+	/*
+	 * Register before-shmem-exit hook to ensure releasing and cleanup
+	 * replication slots while we can still report stats. On dropping a
+	 * replication slot, we report the message to drop the replication slot
+	 * stats.
+	 */
+	before_shmem_exit(ReplicationSlotBeforeShmemExit, 0);
+	callback_registered = true;
+}
+
+/*
+ * Release and cleanup replication slots.
+ */
+static void
+ReplicationSlotBeforeShmemExit(int code, Datum arg)
+{
+	/* Make sure active replication slots are released */
+	if (MyReplicationSlot != NULL)
+		ReplicationSlotRelease();
+
+	/* Also cleanup all the temporary slots. */
+	ReplicationSlotCleanup();
+}
+
 /*
  * Serialize the currently acquired slot's state from memory to disk, thereby
  * guaranteeing the current state will survive a crash.
@@ -972,6 +1023,13 @@ ReplicationSlotsDropDBSlots(Oid dboid)
 	if (max_replication_slots <= 0)
 		return;
 
+	/*
+	 * While dropping replication slots, we acquire a slot to reuse
+	 * ReplicationSlotDropAcquired().  So we register callback to make sure
+	 * releasing the replication slot on exit.
+	 */
+	RegisterReplicationSlotCallback();
+
 restart:
 	LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
 	for (i = 0; i < max_replication_slots; i++)
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index b7d9da0aa9..031a73104e 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -847,13 +847,6 @@ ProcKill(int code, Datum arg)
 	/* Cancel any pending condition variable sleep, too */
 	ConditionVariableCancelSleep();
 
-	/* Make sure active replication slots are released */
-	if (MyReplicationSlot != NULL)
-		ReplicationSlotRelease();
-
-	/* Also cleanup all the temporary slots. */
-	ReplicationSlotCleanup();
-
 	/*
 	 * Detach from any lock group of which we are a member.  If the leader
 	 * exist before all other group members, its PGPROC will remain allocated
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 58b5960e27..36e39c50c0 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4249,8 +4249,9 @@ PostgresMain(int argc, char *argv[],
 		 * We can't release replication slots inside AbortTransaction() as we
 		 * need to be able to start and abort transactions while having a slot
 		 * acquired. But we never need to hold them across top level errors,
-		 * so releasing here is fine. There's another cleanup in ProcKill()
-		 * ensuring we'll correctly cleanup on FATAL errors as well.
+		 * so releasing here is fine. There's another cleanup in
+		 * ReplicationSlotBeforeShmemExit() callback ensuring we'll correctly
+		 * cleanup on FATAL errors as well.
 		 */
 		if (MyReplicationSlot != NULL)
 			ReplicationSlotRelease();
