diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 928f9fe..1d7d942 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -2578,6 +2578,32 @@ find_multixact_start(MultiXactId multi)
 }
 
 /*
+ * Returns an instantaneous snapshot of the current number of active
+ * multixacts and the number of members in the members SLRU area.
+ */
+void
+ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members)
+{
+	MultiXactOffset nextOffset;
+	MultiXactOffset oldestOffset;
+	MultiXactId oldestMultiXactId;
+	MultiXactId nextMultiXactId;
+
+	LWLockAcquire(MultiXactGenLock, LW_SHARED);
+	nextOffset = MultiXactState->nextOffset;
+	oldestMultiXactId = MultiXactState->oldestMultiXactId;
+	nextMultiXactId = MultiXactState->nextMXact;
+	LWLockRelease(MultiXactGenLock);
+	/*
+	 * XXX: Could we store oldestMultiXactMemberOffset in shmem and
+	 * pg_controdata, alongside oldestMultiXactId?
+	 */
+	oldestOffset = find_multixact_start(oldestMultiXactId);
+	*members = nextOffset - oldestOffset;
+	*multixacts = nextMultiXactId - oldestMultiXactId;
+}
+
+/*
  * SlruScanDirectory callback.
  *		This callback deletes segments that are outside the range determined by
  *		the given page numbers.
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index be4cd1d..0592817 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -297,10 +297,12 @@ static void do_autovacuum(void);
 static void FreeWorkerInfo(int code, Datum arg);
 
 static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
-					  TupleDesc pg_class_desc);
+											TupleDesc pg_class_desc,
+											int max_multixact_age_to_avoid_member_wrap);
 static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 						  Form_pg_class classForm,
 						  PgStat_StatTabEntry *tabentry,
+						  int multixact_freeze_age_to_avoid_member_wrap,
 						  bool *dovacuum, bool *doanalyze, bool *wraparound);
 
 static void autovacuum_do_vac_analyze(autovac_table *tab,
@@ -1052,6 +1054,68 @@ db_comparator(const void *a, const void *b)
 }
 
 /*
+ * Computes a multixact age that we can use to trigger earlier wraparound
+ * vacuums than usual, if special action is required to avoid impending
+ * exhaustion of the addressing space of multixact members (caused by large
+ * multixacts).
+ *
+ * If less than a "safe member count" is active, then we return a number at
+ * least as large as autovacuum_multixact_freeze_max_age, so that no special
+ * action is taken.  This should apply to most typical users who don't make
+ * use of large multixacts.
+ *
+ * If more than the "dangerous member count" is active, then we return a max
+ * freeze age of zero to trigger aggressive wraparound vacuuming.
+ *
+ * In between, we return the current number of active multixids scaled down
+ * linearly for higher usage fractions, so that vacuuming becomes more
+ * aggressive as the member SLRU grows, in the hope that different tables will
+ * be vacuumed at different times due to their varying relminmxid values.
+ *
+ * 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 50% and 75%.
+ */
+#define SAFE_MEMBER_COUNT (MaxMultiXactOffset / 2)
+#define DANGEROUS_MEMBER_COUNT (MaxMultiXactOffset - (SAFE_MEMBER_COUNT / 2))
+int
+compute_max_multixact_age_to_avoid_member_wrap(void)
+{
+	MultiXactOffset members;
+	uint32 multixacts;
+	double fraction;
+
+	ReadMultiXactCounts(&multixacts, &members);
+
+	if (members <= SAFE_MEMBER_COUNT)
+	{
+		/*
+		 * There is no danger of member wrap, so return a number that is not
+		 * lower than autovacuum_multixact_freeze_max_age.
+		 */
+		return autovacuum_multixact_freeze_max_age; /* XXX: MAXINT? */
+	}
+
+	if (members >= DANGEROUS_MEMBER_COUNT)
+	{
+		/* We need a wraparound vacuum for all tables now. */
+		return 0;
+	}
+
+	/*
+	 * Choose a cutoff age which is a fraction of the approximate current
+	 * number of active multixacts.  If we are using an amount of member
+	 * address space near SAFE_MEMBER_COUNT, we use a number close to the
+	 * number of active multixacts, so that only tables with the oldest
+	 * relminmxid values become candidates for wraparound vacuums.  As we get
+	 * closer to DANGEROUS_MEMBER_COUNT, we use a number closer to zero, so
+	 * that more tables become candidates for wraparound vacuums.
+	 */
+	fraction = (double) (members - SAFE_MEMBER_COUNT) /
+		(double) (DANGEROUS_MEMBER_COUNT - SAFE_MEMBER_COUNT);
+	return (int) (multixacts * (1.0 - fraction));
+}
+
+/*
  * do_start_worker
  *
  * Bare-bones procedure for starting an autovacuum worker from the launcher.
@@ -1118,7 +1182,8 @@ do_start_worker(void)
 
 	/* Also determine the oldest datminmxid we will consider. */
 	recentMulti = ReadNextMultiXactId();
-	multiForceLimit = recentMulti - autovacuum_multixact_freeze_max_age;
+	multiForceLimit = recentMulti - Min(autovacuum_multixact_freeze_max_age,
+										compute_max_multixact_age_to_avoid_member_wrap());
 	if (multiForceLimit < FirstMultiXactId)
 		multiForceLimit -= FirstMultiXactId;
 
@@ -1881,6 +1946,7 @@ do_autovacuum(void)
 	BufferAccessStrategy bstrategy;
 	ScanKeyData key;
 	TupleDesc	pg_class_desc;
+	int max_multixact_age_to_avoid_member_wrap;
 
 	/*
 	 * StartTransactionCommand and CommitTransactionCommand will automatically
@@ -1920,6 +1986,8 @@ do_autovacuum(void)
 		elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
 	dbForm = (Form_pg_database) GETSTRUCT(tuple);
 
+	max_multixact_age_to_avoid_member_wrap = compute_max_multixact_age_to_avoid_member_wrap();
+
 	if (dbForm->datistemplate || !dbForm->datallowconn)
 	{
 		default_freeze_min_age = 0;
@@ -2001,6 +2069,7 @@ do_autovacuum(void)
 
 		/* Check if it needs vacuum or analyze */
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
+								  max_multixact_age_to_avoid_member_wrap,
 								  &dovacuum, &doanalyze, &wraparound);
 
 		/*
@@ -2129,6 +2198,7 @@ do_autovacuum(void)
 											 shared, dbentry);
 
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
+								  max_multixact_age_to_avoid_member_wrap,
 								  &dovacuum, &doanalyze, &wraparound);
 
 		/* ignore analyze for toast tables */
@@ -2235,7 +2305,8 @@ do_autovacuum(void)
 		 * the race condition is not closed but it is very small.
 		 */
 		MemoryContextSwitchTo(AutovacMemCxt);
-		tab = table_recheck_autovac(relid, table_toast_map, pg_class_desc);
+		tab = table_recheck_autovac(relid, table_toast_map, pg_class_desc,
+									max_multixact_age_to_avoid_member_wrap);
 		if (tab == NULL)
 		{
 			/* someone else vacuumed the table, or it went away */
@@ -2442,7 +2513,8 @@ get_pgstat_tabentry_relid(Oid relid, bool isshared, PgStat_StatDBEntry *shared,
  */
 static autovac_table *
 table_recheck_autovac(Oid relid, HTAB *table_toast_map,
-					  TupleDesc pg_class_desc)
+					  TupleDesc pg_class_desc,
+					  int max_multixact_age_to_avoid_member_wrap)
 {
 	Form_pg_class classForm;
 	HeapTuple	classTup;
@@ -2488,6 +2560,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 										 shared, dbentry);
 
 	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
+							  max_multixact_age_to_avoid_member_wrap,
 							  &dovacuum, &doanalyze, &wraparound);
 
 	/* ignore ANALYZE for toast tables */
@@ -2550,6 +2623,16 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 			? avopts->multixact_freeze_table_age
 			: default_multixact_freeze_table_age;
 
+		/*
+		 * Override the multixact freeze settings if we are running out of
+		 * member address space.
+		 */
+		if (max_multixact_age_to_avoid_member_wrap < multixact_freeze_table_age)
+		{
+			multixact_freeze_table_age = max_multixact_age_to_avoid_member_wrap;
+			multixact_freeze_min_age = 0;
+		}
+
 		tab = palloc(sizeof(autovac_table));
 		tab->at_relid = relid;
 		tab->at_vacoptions = VACOPT_SKIPTOAST |
@@ -2624,6 +2707,7 @@ relation_needs_vacanalyze(Oid relid,
 						  AutoVacOpts *relopts,
 						  Form_pg_class classForm,
 						  PgStat_StatTabEntry *tabentry,
+						  int max_multixact_age_to_avoid_member_wrap,
  /* output params below */
 						  bool *dovacuum,
 						  bool *doanalyze,
@@ -2687,6 +2771,10 @@ relation_needs_vacanalyze(Oid relid,
 		? Min(relopts->multixact_freeze_max_age, autovacuum_multixact_freeze_max_age)
 		: autovacuum_multixact_freeze_max_age;
 
+	/* Special settings if we are running out of member address space. */
+	if (max_multixact_age_to_avoid_member_wrap < multixact_freeze_max_age)
+		multixact_freeze_max_age = max_multixact_age_to_avoid_member_wrap;
+
 	av_enabled = (relopts ? relopts->enabled : true);
 
 	/* Force vacuum if table is at risk of wraparound */
diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h
index 640b198..0500435 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 void ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members);
 
 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..7fc7bd3 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 compute_max_multixact_age_to_avoid_member_wrap(void);
+
 #ifdef EXEC_BACKEND
 extern void AutoVacLauncherMain(int argc, char *argv[]) pg_attribute_noreturn();
 extern void AutoVacWorkerMain(int argc, char *argv[]) pg_attribute_noreturn();
