diff src/backend/utils/sort/tuplesort.c
index d5a2003..27d833f
*** a/src/backend/utils/sort/tuplesort.c
--- b/src/backend/utils/sort/tuplesort.c
*************** struct Tuplesortstate
*** 275,280 ****
--- 275,281 ----
  	SortTuple  *memtuples;		/* array of SortTuple structs */
  	int			memtupcount;	/* number of tuples currently present */
  	int			memtupsize;		/* allocated length of memtuples array */
+ 	bool		fin_growth;		/* are we no longer growing memtupsize? */
  
  	/*
  	 * While building initial runs, this is the current output run number
*************** tuplesort_begin_common(int workMem, bool
*** 569,574 ****
--- 570,576 ----
  
  	state->memtupcount = 0;
  	state->memtupsize = 1024;	/* initial guess */
+ 	state->fin_growth = false;
  	state->memtuples = (SortTuple *) palloc(state->memtupsize * sizeof(SortTuple));
  
  	USEMEM(state, GetMemoryChunkSpace(state->memtuples));
*************** tuplesort_end(Tuplesortstate *state)
*** 956,965 ****
   * 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.
   */
  static bool
  grow_memtuples(Tuplesortstate *state)
--- 958,968 ----
   * Grow the memtuples[] array, if possible within our memory constraint.
   * Return TRUE if able to enlarge the array, FALSE if not.
   *
!  * At each increment (except, possibly, the last) we double the size of the
!  * array.  When we are short on memory we consider smaller increases, but
!  * because availMem moves around with tuple addition/removal, this might result
!  * in thrashing, which is why it will only occur once at the most.  Small
!  * increases in the array size are likely to be pretty inefficient.
   */
  static bool
  grow_memtuples(Tuplesortstate *state)
*************** grow_memtuples(Tuplesortstate *state)
*** 973,990 ****
  	 * 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;
  
  	/*
  	 * 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))
  		return false;
  
  	FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
! 	state->memtupsize *= 2;
  	state->memtuples = (SortTuple *)
  		repalloc(state->memtuples,
  				 state->memtupsize * sizeof(SortTuple));
--- 976,1028 ----
  	 * enough to force palloc to treat it as a separate chunk, so this
  	 * assumption should be good.  But let's check it.)
  	 */
! 	int newmemtupsize;
! 
! 	if (state->fin_growth)
! 	{
  		return false;
+ 	}
+ 	else if (state->availMem < state->allowedMem / 2)
+ 	{
+ 		/*
+ 		 * Make a last-ditch effort to avoid exceeding allowedMem - abandon
+ 		 * doubling strategy.
+ 		 *
+ 		 * Once we are approaching the final growth of memtuples, use the known
+ 		 * size of the tuples seen so far to estimate final growth size.  This
+ 		 * should be on average more space-efficient, which is particularly
+ 		 * important here.
+ 		 *
+ 		 * N.B. We rely on the assumption that nothing other than memtuples and
+ 		 * individual tuple storage has been deducted from availMem.
+ 		 */
+ 		float oldmemtupsize	=	(float) state->memtupsize;
+ 		float allowedMem 	=	(float) state->allowedMem;
+ 		float memNowUsed	=	(float) state->allowedMem - state->availMem;
+ 
+ 		Assert(memNowUsed > 0);
+ 
+ 		/*
+ 		 * XXX: This feels quite brittle; is there a better principled approach,
+ 		 * that does not violate modularity?
+ 		 */
+ 		newmemtupsize = (int) floor(oldmemtupsize * allowedMem / memNowUsed);
+ 		state->fin_growth = true;
+ 	}
+ 	else
+ 	{
+ 		newmemtupsize = state->memtupsize * 2;
+ 	}
  
  	/*
  	 * On a 64-bit machine, allowedMem could be high enough to get us into
  	 * trouble with MaxAllocSize, too.
  	 */
! 	if ((Size) (newmemtupsize) >= MaxAllocSize / sizeof(SortTuple))
  		return false;
  
  	FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
! 	state->memtupsize = newmemtupsize;
  	state->memtuples = (SortTuple *)
  		repalloc(state->memtuples,
  				 state->memtupsize * sizeof(SortTuple));
