diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index c8faa17..634224b 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -2574,6 +2574,29 @@ find_multixact_start(MultiXactId multi)
 }
 
 /*
+ * Returns an instantaneous snapshot of the current number of members in the
+ * members SLRU area.
+ */
+MultiXactOffset
+ReadMultiXactMemberCount(void)
+{
+	MultiXactOffset nextOffset;
+	MultiXactOffset oldestOffset;
+	MultiXactId oldestMultiXactId;
+
+	LWLockAcquire(MultiXactGenLock, LW_SHARED);
+	nextOffset = MultiXactState->nextOffset;
+	oldestMultiXactId = MultiXactState->oldestMultiXactId;
+	LWLockRelease(MultiXactGenLock);
+	/*
+	 * TODO: In future, could oldestMultiXactMemberOffset be stored in shmem,
+	 * pg_controdata, alongside oldestMultiXactId?
+	 */
+	oldestOffset = find_multixact_start(oldestMultiXactId);
+	return nextOffset - oldestOffset;
+}
+
+/*
  * SlruScanDirectory callback.
  *		This callback deletes segments that are outside the range determined by
  *		the given page numbers.
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 7ead161..3502a7f 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -601,7 +601,7 @@ vacuum_set_xid_limits(Relation rel,
 		if (freezetable < 0)
 			freezetable = vacuum_multixact_freeze_table_age;
 		freezetable = Min(freezetable,
-						  autovacuum_multixact_freeze_max_age * 0.95);
+						  autovacuum_multixact_freeze_max_age_adjusted() * 0.95);
 		Assert(freezetable >= 0);
 
 		/*
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index be4cd1d..9848ce7 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1052,6 +1052,39 @@ db_comparator(const void *a, const void *b)
 }
 
 /*
+ * Returns vacuum_multixact_freeze_max_age, adjusted down to prevent excessive use
+ * of addressable multixact member space if required.
+ *
+ * The goal is to avoid the situation where new multixacts can't be created
+ * because the offsets used to address pg_multixact/members would wrap around.
+ *
+ * If less than "minimum safe member count" of the total available member
+ * space is used, then we make no adjustment (this should apply to most
+ * typical users).  If more than the "max safe member count" of the total
+ * available member space is used, then we use a freeze max age of zero to
+ * trigger aggressive vacuuming.  In between, we scale the given freeze max
+ * age down linearly, so that vacuuming becomes more aggressive as the member
+ * SLRU grows.
+ *
+ * Based on the assumption that there is no reasonable way for an end user to
+ * configure the thresholds for this, we just define constants at 25% and 75%.
+ */
+#define MIN_SAFE_MEMBER_COUNT (MaxMultiXactOffset / 4)
+#define MAX_SAFE_MEMBER_COUNT (MaxMultiXactOffset - MIN_SAFE_MEMBER_COUNT)
+int
+autovacuum_multixact_freeze_max_age_adjusted()
+{
+	MultiXactOffset mxact_member_count = ReadMultiXactMemberCount();
+	if (mxact_member_count <= MIN_SAFE_MEMBER_COUNT)
+		return autovacuum_multixact_freeze_max_age;
+	else if (mxact_member_count >= MAX_SAFE_MEMBER_COUNT)
+		return 0;
+	else return (int) (autovacuum_multixact_freeze_max_age
+					   * (((double) (MAX_SAFE_MEMBER_COUNT - mxact_member_count))
+						  / ((double) (MAX_SAFE_MEMBER_COUNT - MIN_SAFE_MEMBER_COUNT))));
+}
+
+/*
  * do_start_worker
  *
  * Bare-bones procedure for starting an autovacuum worker from the launcher.
@@ -1118,7 +1151,7 @@ do_start_worker(void)
 
 	/* Also determine the oldest datminmxid we will consider. */
 	recentMulti = ReadNextMultiXactId();
-	multiForceLimit = recentMulti - autovacuum_multixact_freeze_max_age;
+	multiForceLimit = recentMulti - autovacuum_multixact_freeze_max_age_adjusted();
 	if (multiForceLimit < FirstMultiXactId)
 		multiForceLimit -= FirstMultiXactId;
 
@@ -1931,7 +1964,9 @@ do_autovacuum(void)
 	{
 		default_freeze_min_age = vacuum_freeze_min_age;
 		default_freeze_table_age = vacuum_freeze_table_age;
-		default_multixact_freeze_min_age = vacuum_multixact_freeze_min_age;
+		default_multixact_freeze_min_age =
+			Min(vacuum_multixact_freeze_min_age,
+				autovacuum_multixact_freeze_max_age_adjusted());
 		default_multixact_freeze_table_age = vacuum_multixact_freeze_table_age;
 	}
 
@@ -2684,8 +2719,8 @@ relation_needs_vacanalyze(Oid relid,
 		: autovacuum_freeze_max_age;
 
 	multixact_freeze_max_age = (relopts && relopts->multixact_freeze_max_age >= 0)
-		? Min(relopts->multixact_freeze_max_age, autovacuum_multixact_freeze_max_age)
-		: autovacuum_multixact_freeze_max_age;
+		? Min(relopts->multixact_freeze_max_age, autovacuum_multixact_freeze_max_age_adjusted())
+		: autovacuum_multixact_freeze_max_age_adjusted();
 
 	av_enabled = (relopts ? relopts->enabled : true);
 
diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h
index 640b198..c4afbc2 100644
--- a/src/include/access/multixact.h
+++ b/src/include/access/multixact.h
@@ -126,6 +126,7 @@ extern void MultiXactAdvanceNextMXact(MultiXactId minMulti,
 						  MultiXactOffset minMultiOffset);
 extern void MultiXactAdvanceOldest(MultiXactId oldestMulti, Oid oldestMultiDB);
 extern void MultiXactSetSafeTruncate(MultiXactId safeTruncateMulti);
+extern MultiXactOffset ReadMultiXactMemberCount(void);
 
 extern void multixact_twophase_recover(TransactionId xid, uint16 info,
 						   void *recdata, uint32 len);
diff --git a/src/include/postmaster/autovacuum.h b/src/include/postmaster/autovacuum.h
index 6eaaf4c..71931ef 100644
--- a/src/include/postmaster/autovacuum.h
+++ b/src/include/postmaster/autovacuum.h
@@ -53,6 +53,8 @@ extern void AutoVacWorkerFailed(void);
 /* autovacuum cost-delay balancer */
 extern void AutoVacuumUpdateDelay(void);
 
+extern int autovacuum_multixact_freeze_max_age_adjusted(void);
+
 #ifdef EXEC_BACKEND
 extern void AutoVacLauncherMain(int argc, char *argv[]) pg_attribute_noreturn();
 extern void AutoVacWorkerMain(int argc, char *argv[]) pg_attribute_noreturn();
