I finally found the issue. There is an optional web.config configuration section for the ASP.NET cache:http://msdn.microsoft.com/en-us/library/ms164606.aspx
The default configuration is absolutely nonsense for 32-bit boxes with more than 2 GB of RAM: it would start to throw out items of the cache, if less than 10% of physical RAM is available. Having e. g. 4 GB of RAM installed and a 2 GB limit per process, this is never going to happen. So the cache size (and therefore heap size, mostly generation 2 heap) is increasing and increasing until you run into OutOfMemoryExceptions, or you reach your app pool limit.
Now I added this to my web.config:
<cache privateBytesLimit="400000000" privateBytesPollTime="00:01:00" />
Additionally, I have set the following limits for the application pool:
- 1400 MB virtual memory limit
- 800 MB used memory limit (private bytes)
See this great article for background on these values: http://msdn.microsoft.com/en-us/library/ms972959#monitor_perf_topic10
Now, if I load test my application in a way that a lot of objects will be added to the cache in short time, I can see the ASP.NET cache trims performance counter going up, and memory usage constantly stays under my restart limits.
t.name AS 'Table',
SUM(i.user_seeks + i.user_scans + i.user_lookups)
AS 'Total accesses',
SUM(i.user_seeks) AS 'Seeks',
SUM(i.user_scans) AS 'Scans',
SUM(i.user_lookups) AS 'Lookups'
sys.dm_db_index_usage_stats i RIGHT OUTER JOIN
sys.tables t ON (t.object_id = i.object_id)
ORDER BY [Total accesses]