diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 13e076c..aca28cc 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1737,6 +1737,26 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-relation-cache-max-size" xreflabel="relation_cache_max_size">
+      <term><varname>relation_cache_max_size</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>relation_cache_max_size</varname> configuration
+       parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the maximum total amount of memory allowed for all system
+        relation caches in kilobytes. The value defaults to 0, indicating that
+        pruning by this parameter is disabled at all. After the amount of
+        memory used by all relation caches exceed this size, a new cache entry
+        creation will remove one or more not-recently-used cache entries. This
+        means frequent creation of new cache entry may lead to a slight
+        slowdown of queries.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-max-stack-depth" xreflabel="max_stack_depth">
       <term><varname>max_stack_depth</varname> (<type>integer</type>)
       <indexterm>
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 6a85e14..98df265 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -200,12 +200,12 @@ RelationBuildRowSecurity(Relation relation)
 	 * relation's row security policy.  This makes it easy to clean up during
 	 * a relcache flush.
 	 */
-	rscxt = AllocSetContextCreate(CacheMemoryContext,
+	rscxt = AllocSetContextCreate(RelCacheMemoryContext,
 								  "row security descriptor",
 								  ALLOCSET_SMALL_SIZES);
 
 	/*
-	 * Since rscxt lives under CacheMemoryContext, it is long-lived.  Use a
+	 * Since rscxt lives under RelCacheMemoryContext, it is long-lived.  Use a
 	 * PG_TRY block to ensure it'll get freed if we fail partway through.
 	 */
 	PG_TRY();
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 3ae2640..57ac2ef 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1921,7 +1921,7 @@ EnableDisableTrigger(Relation rel, const char *tgname,
  * Build trigger data to attach to the given relcache entry.
  *
  * Note that trigger data attached to a relcache entry must be stored in
- * CacheMemoryContext to ensure it survives as long as the relcache entry.
+ * RelCacheMemoryContext to ensure it survives as long as the relcache entry.
  * But we should be running in a less long-lived working context.  To avoid
  * leaking cache memory if this routine fails partway through, we build a
  * temporary TriggerDesc in working memory and then copy the completed
@@ -2067,7 +2067,7 @@ RelationBuildTriggers(Relation relation)
 		SetTriggerFlags(trigdesc, &(triggers[i]));
 
 	/* Copy completed trigdesc into cache storage */
-	oldContext = MemoryContextSwitchTo(CacheMemoryContext);
+	oldContext = MemoryContextSwitchTo(RelCacheMemoryContext);
 	relation->trigdesc = CopyTriggerDesc(trigdesc);
 	MemoryContextSwitchTo(oldContext);
 
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index c917ec4..eba5a25 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -434,8 +434,8 @@ GetFdwRoutineForRelation(Relation relation, bool makecopy)
 		/* Get the info by consulting the catalogs and the FDW code */
 		fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation));
 
-		/* Save the data for later reuse in CacheMemoryContext */
-		cfdwroutine = (FdwRoutine *) MemoryContextAlloc(CacheMemoryContext,
+		/* Save the data for later reuse in RelCacheMemoryContext */
+		cfdwroutine = (FdwRoutine *) MemoryContextAlloc(RelCacheMemoryContext,
 														sizeof(FdwRoutine));
 		memcpy(cfdwroutine, fdwroutine, sizeof(FdwRoutine));
 		relation->rd_fdwroutine = cfdwroutine;
diff --git a/src/backend/partitioning/partdesc.c b/src/backend/partitioning/partdesc.c
index e436d1e..9238be0 100644
--- a/src/backend/partitioning/partdesc.c
+++ b/src/backend/partitioning/partdesc.c
@@ -174,7 +174,7 @@ RelationBuildPartitionDesc(Relation rel)
 	}
 
 	/* Now build the actual relcache partition descriptor */
-	rel->rd_pdcxt = AllocSetContextCreate(CacheMemoryContext,
+	rel->rd_pdcxt = AllocSetContextCreate(RelCacheMemoryContext,
 										  "partition descriptor",
 										  ALLOCSET_SMALL_SIZES);
 	MemoryContextCopyAndSetIdentifier(rel->rd_pdcxt,
diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c
index 8f43d68..c6f7f8f 100644
--- a/src/backend/utils/cache/partcache.c
+++ b/src/backend/utils/cache/partcache.c
@@ -45,10 +45,10 @@ static List *generate_partition_qual(Relation rel);
  *
  * Partitioning key data is a complex structure; to avoid complicated logic to
  * free individual elements whenever the relcache entry is flushed, we give it
- * its own memory context, child of CacheMemoryContext, which can easily be
+ * its own memory context, child of RelCacheMemoryContext, which can easily be
  * deleted on its own.  To avoid leaking memory in that context in case of an
  * error partway through this function, the context is initially created as a
- * child of CurTransactionContext and only re-parented to CacheMemoryContext
+ * child of CurTransactionContext and only re-parented to RelCacheMemoryContext
  * at the end, when no further errors are possible.  Also, we don't make this
  * context the current context except in very brief code sections, out of fear
  * that some of our callees allocate memory on their own which would be leaked
@@ -239,7 +239,7 @@ RelationBuildPartitionKey(Relation relation)
 	 * Success --- reparent our context and make the relcache point to the
 	 * newly constructed key
 	 */
-	MemoryContextSetParent(partkeycxt, CacheMemoryContext);
+	MemoryContextSetParent(partkeycxt, RelCacheMemoryContext);
 	relation->rd_partkeycxt = partkeycxt;
 	relation->rd_partkey = key;
 }
@@ -374,7 +374,7 @@ generate_partition_qual(Relation rel)
 		elog(ERROR, "unexpected whole-row reference found in partition key");
 
 	/* Save a copy in the relcache */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 	rel->rd_partcheck = copyObject(result);
 	MemoryContextSwitchTo(oldcxt);
 
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 64f3c2e..9ee2f4f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -135,6 +135,15 @@ typedef struct relidcacheent
 static HTAB *RelationIdCache;
 
 /*
+ * GUC for limit by the number of entries. Entries are managed by LRU list
+ * and removed when the number of them goes above relation_cache_max_size
+ * in kilobytes
+ */
+int relation_cache_max_size = 0;
+
+static dlist_head rel_lruhead = DLIST_STATIC_INIT(rel_lruhead);
+
+/*
  * This flag is false until we have prepared the critical relcache entries
  * that are needed to do indexscans on the tables read by relcache building.
  */
@@ -201,6 +210,12 @@ do { \
 		Relation _old_rel = hentry->reldesc; \
 		Assert(replace_allowed); \
 		hentry->reldesc = (RELATION); \
+		/* nailed entries are excluded from LRU list not to be blown away */ \
+		if (!((RELATION)->rd_isnailed)) \
+		{ \
+			dlist_push_tail(&rel_lruhead, &((RELATION)->rd_lrunode)); \
+			dlist_delete(&_old_rel->rd_lrunode); \
+		} \
 		if (RelationHasReferenceCountZero(_old_rel)) \
 			RelationDestroyRelation(_old_rel, false); \
 		else if (!IsBootstrapProcessingMode()) \
@@ -208,7 +223,11 @@ do { \
 				 RelationGetRelationName(_old_rel)); \
 	} \
 	else \
+	{ \
 		hentry->reldesc = (RELATION); \
+		if (!((RELATION)->rd_isnailed)) \
+			dlist_push_tail(&rel_lruhead, &((RELATION)->rd_lrunode)); \
+	} \
 } while(0)
 
 #define RelationIdCacheLookup(ID, RELATION) \
@@ -218,7 +237,11 @@ do { \
 										   (void *) &(ID), \
 										   HASH_FIND, NULL); \
 	if (hentry) \
+	{ \
 		RELATION = hentry->reldesc; \
+		if (!(RELATION)->rd_isnailed) \
+			dlist_move_tail(&rel_lruhead, &((RELATION)->rd_lrunode)); \
+	} \
 	else \
 		RELATION = NULL; \
 } while(0)
@@ -229,6 +252,8 @@ do { \
 	hentry = (RelIdCacheEnt *) hash_search(RelationIdCache, \
 										   (void *) &((RELATION)->rd_id), \
 										   HASH_REMOVE, NULL); \
+	if (hentry && !(RELATION)->rd_isnailed) \
+		dlist_delete(&(RELATION)->rd_lrunode); \
 	if (hentry == NULL) \
 		elog(WARNING, "failed to delete relcache entry for OID %u", \
 			 (RELATION)->rd_id); \
@@ -297,6 +322,8 @@ static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid,
 				  StrategyNumber numSupport);
 static void RelationCacheInitFileRemoveInDir(const char *tblspcpath);
 static void unlink_initfile(const char *initfilename, int elevel);
+static void CreateRelCacheMemoryContext(void);
+static void CleanupOldRelationCache(void);
 
 
 /*
@@ -390,8 +417,8 @@ AllocateRelationDesc(Form_pg_class relp)
 	MemoryContext oldcxt;
 	Form_pg_class relationForm;
 
-	/* Relcache entries must live in CacheMemoryContext */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	/* Relcache entries must live in RelCacheMemoryContext */
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 
 	/*
 	 * allocate and zero space for new relation descriptor
@@ -482,7 +509,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	if (options)
 	{
-		relation->rd_options = MemoryContextAlloc(CacheMemoryContext,
+		relation->rd_options = MemoryContextAlloc(RelCacheMemoryContext,
 												  VARSIZE(options));
 		memcpy(relation->rd_options, options, VARSIZE(options));
 		pfree(options);
@@ -512,7 +539,7 @@ RelationBuildTupleDesc(Relation relation)
 	relation->rd_att->tdtypeid = relation->rd_rel->reltype;
 	relation->rd_att->tdtypmod = -1;	/* unnecessary, but... */
 
-	constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
+	constr = (TupleConstr *) MemoryContextAlloc(RelCacheMemoryContext,
 												sizeof(TupleConstr));
 	constr->has_not_null = false;
 	constr->has_generated_stored = false;
@@ -576,7 +603,7 @@ RelationBuildTupleDesc(Relation relation)
 		{
 			if (attrdef == NULL)
 				attrdef = (AttrDefault *)
-					MemoryContextAllocZero(CacheMemoryContext,
+					MemoryContextAllocZero(RelCacheMemoryContext,
 										   RelationGetNumberOfAttributes(relation) *
 										   sizeof(AttrDefault));
 			attrdef[ndef].adnum = attnum;
@@ -606,7 +633,7 @@ RelationBuildTupleDesc(Relation relation)
 
 				if (attrmiss == NULL)
 					attrmiss = (AttrMissing *)
-						MemoryContextAllocZero(CacheMemoryContext,
+						MemoryContextAllocZero(RelCacheMemoryContext,
 											   relation->rd_rel->relnatts *
 											   sizeof(AttrMissing));
 
@@ -627,7 +654,7 @@ RelationBuildTupleDesc(Relation relation)
 				else
 				{
 					/* otherwise copy in the correct context */
-					oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+					oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 					attrmiss[attnum - 1].am_value = datumCopy(missval,
 															  attp->attbyval,
 															  attp->attlen);
@@ -700,7 +727,7 @@ RelationBuildTupleDesc(Relation relation)
 		{
 			constr->num_check = relation->rd_rel->relchecks;
 			constr->check = (ConstrCheck *)
-				MemoryContextAllocZero(CacheMemoryContext,
+				MemoryContextAllocZero(RelCacheMemoryContext,
 									   constr->num_check * sizeof(ConstrCheck));
 			CheckConstraintFetch(relation);
 		}
@@ -747,7 +774,7 @@ RelationBuildRuleLock(Relation relation)
 	/*
 	 * Make the private context.  Assume it'll not contain much data.
 	 */
-	rulescxt = AllocSetContextCreate(CacheMemoryContext,
+	rulescxt = AllocSetContextCreate(RelCacheMemoryContext,
 									 "relation rules",
 									 ALLOCSET_SMALL_SIZES);
 	relation->rd_rulescxt = rulescxt;
@@ -1011,6 +1038,48 @@ equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2)
 }
 
 /*
+ *		CleaunupOldRelationCache
+ *		Clean up old entries if the amount of memory exceeds relation_cache_max_size
+ *		to prevent relcache from bloating.
+ */
+static void
+CleanupOldRelationCache(void)
+{
+	Relation relation;
+	dlist_mutable_iter iter;
+
+	if (relation_cache_max_size == 0 ||
+		MemoryContextGetUsedspace(RelCacheMemoryContext) <=
+		(Size) relation_cache_max_size * 1024)
+		return;
+
+	/* Scan over LRU to find entries to remove */
+	dlist_foreach_modify(iter, &rel_lruhead)
+	{
+		relation = dlist_container(RelationData, rd_lrunode, iter.cur);
+
+		/* check against global size */
+		if (MemoryContextGetUsedspace(RelCacheMemoryContext) <=
+			(Size) relation_cache_max_size * 1024)
+			break;
+
+		/*
+		 * We don't remove referenced entries. Nailed entries are not removed
+		 * as well but we don't care because nailed ones are not added to LRU
+		 * list in the first place.
+		 */
+		if (!RelationHasReferenceCountZero(relation))
+			continue;
+			
+		elog(DEBUG1, "pruning relation cache (id = %d, %s)",
+			 relation->rd_id, RelationGetRelationName(relation));
+		RelationClearRelation(relation, false);
+	}
+	elog(DEBUG1, "current RelCacheMemoryContext size (kB): %d",
+		 (int) MemoryContextGetUsedspace(RelCacheMemoryContext) / 1024);
+}
+
+/*
  *		RelationBuildDesc
  *
  *		Build a relation descriptor.  The caller must hold at least
@@ -1243,7 +1312,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	 * we'll elog a WARNING and leak the already-present entry.
 	 */
 	if (insertIt)
+	{
+		/*
+		 * Before trying to expand hash table, cleanup old entries to supress
+		 * memory usage under relation_cache_max_size
+		 */
+		CleanupOldRelationCache();
 		RelationCacheInsert(relation, true);
+	}
 
 	/* It's fully valid */
 	relation->rd_isvalid = true;
@@ -1383,7 +1459,7 @@ RelationInitIndexAccessInfo(Relation relation)
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "cache lookup failed for index %u",
 			 RelationGetRelid(relation));
-	oldcontext = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcontext = MemoryContextSwitchTo(RelCacheMemoryContext);
 	relation->rd_indextuple = heap_copytuple(tuple);
 	relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple);
 	MemoryContextSwitchTo(oldcontext);
@@ -1411,7 +1487,7 @@ RelationInitIndexAccessInfo(Relation relation)
 	 * a context, and not just a couple of pallocs, is so that we won't leak
 	 * any subsidiary info attached to fmgr lookup records.
 	 */
-	indexcxt = AllocSetContextCreate(CacheMemoryContext,
+	indexcxt = AllocSetContextCreate(RelCacheMemoryContext,
 									 "index info",
 									 ALLOCSET_SMALL_SIZES);
 	relation->rd_indexcxt = indexcxt;
@@ -1795,7 +1871,7 @@ RelationInitTableAccessMethod(Relation relation)
  * during bootstrap or before RelationCacheInitializePhase3 runs, and none of
  * these properties matter then...)
  *
- * NOTE: we assume we are already switched into CacheMemoryContext.
+ * NOTE: we assume we are already switched into RelCacheMemoryContext.
  */
 static void
 formrdesc(const char *relationName, Oid relationReltype,
@@ -2341,6 +2417,7 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 		else
 			FreeTupleDesc(relation->rd_att);
 	}
+
 	FreeTriggerDesc(relation->trigdesc);
 	list_free_deep(relation->rd_fkeylist);
 	list_free(relation->rd_indexlist);
@@ -2580,7 +2657,7 @@ RelationClearRelation(Relation relation, bool rebuild)
 			memcpy(newrel, relation, sizeof(RelationData));
 			memcpy(relation, &tmpstruct, sizeof(RelationData));
 		}
-
+		
 		/* rd_smgr must not be swapped, due to back-links from smgr level */
 		SWAPFIELD(SMgrRelation, rd_smgr);
 		/* rd_refcnt must be preserved */
@@ -2590,6 +2667,8 @@ RelationClearRelation(Relation relation, bool rebuild)
 		/* creation sub-XIDs must be preserved */
 		SWAPFIELD(SubTransactionId, rd_createSubid);
 		SWAPFIELD(SubTransactionId, rd_newRelfilenodeSubid);
+		/* LRU node should be preserved because it's still in dlinked list */
+		SWAPFIELD(dlist_node, rd_lrunode);
 		/* un-swap rd_rel pointers, swap contents instead */
 		SWAPFIELD(Form_pg_class, rd_rel);
 		/* ... but actually, we don't have to update newrel->rd_rel */
@@ -2888,7 +2967,7 @@ RememberToFreeTupleDescAtEOX(TupleDesc td)
 	{
 		MemoryContext oldcxt;
 
-		oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+		oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 
 		EOXactTupleDescArray = (TupleDesc *) palloc(16 * sizeof(TupleDesc));
 		EOXactTupleDescArrayLen = 16;
@@ -3246,10 +3325,10 @@ RelationBuildLocalRelation(const char *relname,
 	/*
 	 * switch to the cache context to create the relcache entry.
 	 */
-	if (!CacheMemoryContext)
-		CreateCacheMemoryContext();
+	if (!RelCacheMemoryContext)
+		CreateRelCacheMemoryContext();
 
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 
 	/*
 	 * allocate a new relation descriptor and fill in basic state fields.
@@ -3381,6 +3460,12 @@ RelationBuildLocalRelation(const char *relname,
 		RelationInitTableAccessMethod(rel);
 
 	/*
+	 * Before trying to expand hash table, cleanup old entries to supress
+	 * memory usage under relation_cache_max_size
+	 */
+	CleanupOldRelationCache();
+
+	/*
 	 * Okay to insert into the relcache hash table.
 	 *
 	 * Ordinarily, there should certainly not be an existing hash entry for
@@ -3568,10 +3653,10 @@ RelationCacheInitialize(void)
 	HASHCTL		ctl;
 
 	/*
-	 * make sure cache memory context exists
+	 * make sure relation cache memory context exists
 	 */
-	if (!CacheMemoryContext)
-		CreateCacheMemoryContext();
+	if (!RelCacheMemoryContext)
+		CreateRelCacheMemoryContext();
 
 	/*
 	 * create hashtable that indexes the relcache
@@ -3617,9 +3702,9 @@ RelationCacheInitializePhase2(void)
 		return;
 
 	/*
-	 * switch to cache memory context
+	 * switch to relation cache memory context
 	 */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 
 	/*
 	 * Try to load the shared relcache cache file.  If unsuccessful, bootstrap
@@ -3672,9 +3757,9 @@ RelationCacheInitializePhase3(void)
 	RelationMapInitializePhase3();
 
 	/*
-	 * switch to cache memory context
+	 * switch to relation cache memory context
 	 */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 
 	/*
 	 * Try to load the local relcache cache file.  If unsuccessful, bootstrap
@@ -3959,6 +4044,28 @@ RelationCacheInitializePhase3(void)
 }
 
 /*
+ * Routine for creating relation cache context
+ * if it doesn't exist yet.
+ *
+ * RelCacheMemoryContext is set under CacheMemoryContext 
+ * to calculate memory usage easily and make MemoryContextStats() output
+ * organized.
+ */
+static void
+CreateRelCacheMemoryContext(void)
+{
+	if (!CacheMemoryContext)
+		CreateCacheMemoryContext();
+	if (!RelCacheMemoryContext)
+	{
+		RelCacheMemoryContext = AllocSetContextCreate(CacheMemoryContext,
+												   "RelCacheMemoryContext",
+												   ALLOCSET_DEFAULT_SIZES);
+		MemoryContextSetCollectGroupSize(RelCacheMemoryContext);
+	}
+}
+
+/*
  * Load one critical system index into the relcache
  *
  * indexoid is the OID of the target index, heapoid is the OID of the catalog
@@ -4005,7 +4112,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs)
 	MemoryContext oldcxt;
 	int			i;
 
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 
 	result = CreateTemplateTupleDesc(natts);
 	result->tdtypeid = RECORDOID;	/* not right, but we don't care */
@@ -4109,7 +4216,7 @@ AttrDefaultFetch(Relation relation)
 				/* detoast and convert to cstring in caller's context */
 				char	   *s = TextDatumGetCString(val);
 
-				attrdef[i].adbin = MemoryContextStrdup(CacheMemoryContext, s);
+				attrdef[i].adbin = MemoryContextStrdup(RelCacheMemoryContext, s);
 				pfree(s);
 			}
 			break;
@@ -4164,7 +4271,7 @@ CheckConstraintFetch(Relation relation)
 
 		check[found].ccvalid = conform->convalidated;
 		check[found].ccnoinherit = conform->connoinherit;
-		check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
+		check[found].ccname = MemoryContextStrdup(RelCacheMemoryContext,
 												  NameStr(conform->conname));
 
 		/* Grab and test conbin is actually set */
@@ -4177,7 +4284,7 @@ CheckConstraintFetch(Relation relation)
 
 		/* detoast and convert to cstring in caller's context */
 		s = TextDatumGetCString(val);
-		check[found].ccbin = MemoryContextStrdup(CacheMemoryContext, s);
+		check[found].ccbin = MemoryContextStrdup(RelCacheMemoryContext, s);
 		pfree(s);
 
 		found++;
@@ -4287,7 +4394,7 @@ RelationGetFKeyList(Relation relation)
 	table_close(conrel, AccessShareLock);
 
 	/* Now save a copy of the completed list in the relcache entry. */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 	oldlist = relation->rd_fkeylist;
 	relation->rd_fkeylist = copyObject(result);
 	relation->rd_fkeyvalid = true;
@@ -4406,7 +4513,7 @@ RelationGetIndexList(Relation relation)
 	table_close(indrel, AccessShareLock);
 
 	/* Now save a copy of the completed list in the relcache entry. */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 	oldlist = relation->rd_indexlist;
 	relation->rd_indexlist = list_copy(result);
 	relation->rd_pkindex = pkeyIndex;
@@ -4494,7 +4601,7 @@ RelationGetStatExtList(Relation relation)
 	table_close(indrel, AccessShareLock);
 
 	/* Now save a copy of the completed list in the relcache entry. */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 	oldlist = relation->rd_statlist;
 	relation->rd_statlist = list_copy(result);
 
@@ -4568,7 +4675,7 @@ RelationSetIndexList(Relation relation, List *indexIds)
 
 	Assert(relation->rd_isnailed);
 	/* Copy the list into the cache context (could fail for lack of mem) */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 	indexIds = list_copy(indexIds);
 	MemoryContextSwitchTo(oldcxt);
 	/* Okay to replace old list */
@@ -4987,7 +5094,7 @@ restart:
 	 * leave the relcache entry looking like the other ones are valid but
 	 * empty.
 	 */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 	relation->rd_keyattr = bms_copy(uindexattrs);
 	relation->rd_pkattr = bms_copy(pkindexattrs);
 	relation->rd_idattr = bms_copy(idindexattrs);
@@ -5196,7 +5303,7 @@ GetRelationPublicationActions(Relation relation)
 	}
 
 	/* Now save copy of the actions in the relcache entry. */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheMemoryContext);
 	relation->rd_pubactions = palloc(sizeof(PublicationActions));
 	memcpy(relation->rd_pubactions, pubactions, sizeof(PublicationActions));
 	MemoryContextSwitchTo(oldcxt);
@@ -5332,7 +5439,7 @@ errtableconstraint(Relation rel, const char *conname)
  * criticalSharedRelcachesBuilt to true.
  * If not successful, return false.
  *
- * NOTE: we assume we are already switched into CacheMemoryContext.
+ * NOTE: we assume we are already switched into RelCacheMemoryContext.
  */
 static bool
 load_relcache_init_file(bool shared)
@@ -5501,7 +5608,7 @@ load_relcache_init_file(bool shared)
 			 * prepare index info context --- parameters should match
 			 * RelationInitIndexAccessInfo
 			 */
-			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+			indexcxt = AllocSetContextCreate(RelCacheMemoryContext,
 											 "index info",
 											 ALLOCSET_SMALL_SIZES);
 			rel->rd_indexcxt = indexcxt;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 42d427e..eeecba7 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -91,6 +91,7 @@
 #include "utils/plancache.h"
 #include "utils/portal.h"
 #include "utils/ps_status.h"
+#include "utils/relcache.h"
 #include "utils/rls.h"
 #include "utils/snapmgr.h"
 #include "utils/tzparser.h"
@@ -2256,6 +2257,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"relation_cache_max_size", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Sets the maximum size of relation cache in kilobytes."),
+			NULL,
+			GUC_UNIT_KB
+		},
+		&relation_cache_max_size,
+		0, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
+
 	/*
 	 * We use the hopefully-safely-small value of 100kB as the compiled-in
 	 * default for max_stack_depth.  InitializeGUCOptions will increase it if
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index a3b2e92..d26a269 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -131,6 +131,7 @@
 #catalog_cache_memory_target = 0kB	# in kB
 #catalog_cache_prune_min_age = 300s	# -1 disables pruning
 #catalog_cache_max_size = 0kB  		# in kB
+#relation_cache_max_size = 0kB		# in kB
 #max_stack_depth = 2MB			# min 100kB
 #shared_memory_type = mmap		# the default is the first option
 					# supported by the operating system:
diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index 19f3756..a871b7d 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -262,6 +262,17 @@ static AllocSetFreeList context_freelists[2] =
 	}
 };
 
+/* support macros to account size of descendant */
+#define UpdateParentSize(context, size) \
+do { \
+	Assert((context)->collectGroupSize); \
+	for (MemoryContext cur = (context->parent); \
+		 cur && cur->collectGroupSize; \
+		 cur = cur->parent) \
+				cur->usedspace += size; \
+} while (0)
+
+
 /*
  * These functions implement the MemoryContext API for AllocSet contexts.
  */
@@ -779,6 +790,8 @@ AllocSetAlloc(MemoryContext context, Size size)
 		VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOCCHUNK_PRIVATE_LEN);
 
 		context->usedspace += chunk_size;
+		if (context->collectGroupSize)
+			UpdateParentSize(context, chunk_size);
 
 		return AllocChunkGetPointer(chunk);
 	}
@@ -820,6 +833,8 @@ AllocSetAlloc(MemoryContext context, Size size)
 		VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOCCHUNK_PRIVATE_LEN);
 
 		context->usedspace += chunk->size;
+		if (context->collectGroupSize)
+			UpdateParentSize(context, chunk->size);
 
 		return AllocChunkGetPointer(chunk);
 	}
@@ -981,6 +996,8 @@ AllocSetAlloc(MemoryContext context, Size size)
 	VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOCCHUNK_PRIVATE_LEN);
 
 	context->usedspace += chunk_size;
+	if (context->collectGroupSize)
+		UpdateParentSize(context, chunk_size);
 
 	return AllocChunkGetPointer(chunk);
 }
@@ -1029,6 +1046,9 @@ AllocSetFree(MemoryContext context, void *pointer)
 
 		/* OK, remove block from aset's list and free it */
 		context->usedspace -= chunk->size;
+		if (context->collectGroupSize)
+			UpdateParentSize(context, -chunk->size);
+
 		if (block->prev)
 			block->prev->next = block->next;
 		else
@@ -1047,6 +1067,8 @@ AllocSetFree(MemoryContext context, void *pointer)
 
 		chunk->aset = (void *) set->freelist[fidx];
 		context->usedspace -= chunk->size;
+		if (context->collectGroupSize)
+			UpdateParentSize(context, -chunk->size);
 
 #ifdef CLOBBER_FREED_MEMORY
 		wipe_mem(pointer, chunk->size);
@@ -1168,6 +1190,9 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
 		chksize = MAXALIGN(size);
 		blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
 		context->usedspace -= oldsize;
+		if (context->collectGroupSize)
+				UpdateParentSize(context, -oldsize);
+
 		block = (AllocBlock) realloc(block, blksize);
 		if (block == NULL)
 		{
@@ -1188,6 +1213,9 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
 			block->next->prev = block;
 		chunk->size = chksize;
 		context->usedspace += chksize;
+		if (context->collectGroupSize)
+			UpdateParentSize(context, chksize);
+
 
 #ifdef MEMORY_CONTEXT_CHECKING
 #ifdef RANDOMIZE_ALLOCATED_MEMORY
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 2f02786..fce7a87 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -52,6 +52,9 @@ MemoryContext CurTransactionContext = NULL;
 /* This is a transient link to the active portal's memory context: */
 MemoryContext PortalContext = NULL;
 
+/*  MemoryContext for relcache, whose parent is CacheMemoryContext */
+MemoryContext RelCacheMemoryContext = NULL;
+
 static void MemoryContextCallResetCallbacks(MemoryContext context);
 static void MemoryContextStatsInternal(MemoryContext context, int level,
 						   bool print, int max_children,
@@ -174,6 +177,7 @@ MemoryContextResetOnly(MemoryContext context)
 		context->methods->reset(context);
 		context->usedspace = 0;
 		context->isReset = true;
+		context->collectGroupSize = false;
 		VALGRIND_DESTROY_MEMPOOL(context);
 		VALGRIND_CREATE_MEMPOOL(context, 0, false);
 	}
@@ -758,6 +762,15 @@ MemoryContextCreate(MemoryContext node,
 		node->nextchild = NULL;
 		node->allowInCritSection = false;
 	}
+	
+	/* 
+	 * if this node is descendant of parent which want to
+	 * collect descendant sizes, mark the flag true 
+	 */
+	if (parent && (parent->collectGroupSize))			   
+		node->collectGroupSize = true;
+	else
+		node->collectGroupSize = false;
 
 	VALGRIND_CREATE_MEMPOOL(node, 0, false);
 }
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index 1203780..9f6f809 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -85,6 +85,7 @@ typedef struct MemoryContextData
 	MemoryContext prevchild;	/* previous child of same parent */
 	MemoryContext nextchild;	/* next child of same parent */
 	Size	usedspace;			/* accumulates consumed memory size */
+	bool    collectGroupSize;	/* account size from its descendant */
 	const char *name;			/* context name (just for debugging) */
 	const char *ident;			/* context ID if any (just for debugging) */
 	MemoryContextCallback *reset_cbs;	/* list of reset/delete callbacks */
@@ -109,3 +110,5 @@ typedef struct MemoryContextData
 
 /* Interface routines for memory usedspace-based accounting */
 #define MemoryContextGetUsedspace(c)  ((c)->usedspace)
+
+#define MemoryContextSetCollectGroupSize(c) ((c)->collectGroupSize = true)
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 30d7998..83e6569 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -63,6 +63,13 @@ extern PGDLLIMPORT MemoryContext CurTransactionContext;
 /* This is a transient link to the active portal's memory context: */
 extern PGDLLIMPORT MemoryContext PortalContext;
 
+/* 
+ * MemoryContext for relcache, which is a child of
+ * CacheMemoryContext, used to keep track of memory usage and
+ * prune old relcaches exceeded  
+ */
+extern PGDLLIMPORT MemoryContext RelCacheMemoryContext;
+
 /* Backwards compatibility macro */
 #define MemoryContextResetAndDeleteChildren(ctx) MemoryContextReset(ctx)
 
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 5402851..beb626a 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -20,6 +20,7 @@
 #include "catalog/pg_index.h"
 #include "catalog/pg_publication.h"
 #include "fmgr.h"
+#include "lib/ilist.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
 #include "storage/block.h"
@@ -63,6 +64,7 @@ typedef struct RelationData
 	char		rd_indexvalid;	/* state of rd_indexlist: 0 = not valid, 1 =
 								 * valid, 2 = temporarily forced */
 	bool		rd_statvalid;	/* is rd_statlist valid? */
+	dlist_node	rd_lrunode;		/* LRU node */
 
 	/*
 	 * rd_createSubid is the ID of the highest subtransaction the rel has
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 809d6aa..e724189 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -33,6 +33,8 @@ typedef struct RelationData *Relation;
  */
 typedef Relation *RelationPtr;
 
+
+
 /*
  * Routines to open (lookup) and close a relcache entry
  */
@@ -141,4 +143,7 @@ extern bool criticalRelcachesBuilt;
 /* should be used only by relcache.c and postinit.c */
 extern bool criticalSharedRelcachesBuilt;
 
+/* for guc.c, not PGDLLIPMPORT'ed */
+extern int relation_cache_max_size;
+
 #endif							/* RELCACHE_H */
