diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index ce27e40..a97e3b1 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -957,14 +957,18 @@ tuplesort_end(Tuplesortstate *state)
  * Grow the memtuples[] array, if possible within our memory constraint.
  * Return TRUE if able to enlarge the array, FALSE if not.
  *
- * At each increment we double the size of the array.  When we are short
- * on memory we could consider smaller increases, but because availMem
- * moves around with tuple addition/removal, this might result in thrashing.
- * Small increases in the array size are likely to be pretty inefficient.
+ * At each increment we double the size of the array.  When we are short on
+ * memory we do attempt one last, smaller increase.  This only happens at most
+ * once, since availMem moves around with tuple addition/removal. To do othewise
+ * might result in thrashing.  This is nothing more than a last-ditch effort to
+ * avoid exceeding allowedMem, an undesirable outcome if avoidable.
  */
 static bool
 grow_memtuples(Tuplesortstate *state)
 {
+	int			newmemtupsize;
+	long		memNowUsed = state->allowedMem - state->availMem;
+
 	/*
 	 * We need to be sure that we do not cause LACKMEM to become true, else
 	 * the space management algorithm will go nuts.  We assume here that the
@@ -974,18 +978,50 @@ grow_memtuples(Tuplesortstate *state)
 	 * enough to force palloc to treat it as a separate chunk, so this
 	 * assumption should be good.  But let's check it.)
 	 */
-	if (state->availMem <= (long) (state->memtupsize * sizeof(SortTuple)))
-		return false;
+	if (memNowUsed <= state->availMem)
+		newmemtupsize = state->memtupsize * 2;
+	else
+	{
+		int			memtupsize = state->memtupsize;
+		long		allowedMem = state->allowedMem;
+
+		/*
+		 * For this last increment, abandon doubling strategy.
+		 *
+		 * To make sure LACKMEM(state) doesn't become true, we can't increase
+		 * memtupsize by more than state->availMem/sizeof(SortTuple) elements.
+		 * In practice, we want to increase it by considerably less, because
+		 * we need to leave some space for the tuples to which the new array
+		 * slots will refer.  We assume the new tuples will be about the same
+		 * size as the tuples we've already seen, and thus use the known size
+		 * (in bytes) of the tuples seen so far to estimate an appropriate new
+		 * size for the memtuples array.  The optimal value might be higher or
+		 * lower than we estimate, but it's hard to know that in advance.
+		 *
+		 * In any case, we're definitely safe against enlarging the array so
+		 * much that LACKMEM(state) becomes true, because the memory currently
+		 * used includes the present array; thus, there would be enough
+		 * allowedMem for the new array elements even if no other memory were
+		 * currently used.
+		 */
+		newmemtupsize = memtupsize * allowedMem / memNowUsed;
+
+		Assert(newmemtupsize <= state->memtupsize * 2);
+
+		/* This may not be our first time through */
+		if (newmemtupsize <= memtupsize)
+			return false;
+	}
 
 	/*
 	 * On a 64-bit machine, allowedMem could be high enough to get us into
 	 * trouble with MaxAllocSize, too.
 	 */
-	if ((Size) (state->memtupsize * 2) >= MaxAllocSize / sizeof(SortTuple))
+	if ((Size) (newmemtupsize) >= MaxAllocSize / sizeof(SortTuple))
 		return false;
 
 	FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
-	state->memtupsize *= 2;
+	state->memtupsize = newmemtupsize;
 	state->memtuples = (SortTuple *)
 		repalloc(state->memtuples,
 				 state->memtupsize * sizeof(SortTuple));
