Bug 381

Summary: Memory leak in asp.net with sessions enabled
Product: [Mono] Class Libraries Reporter: Justen Hyde <throctukes>
Component: System.WebAssignee: Marek Habersack <grendel>
Status: NEW ---    
Severity: normal CC: dan.parnham, gonzalo, michael, miguel, mono-bugs+mono, pgentoo
Priority: ---    
Version: 2.10.x   
Target Milestone: Untriaged   
Hardware: PC   
OS: Linux   
Tags: Is this bug a regression?: ---
Last known good build:
Attachments: test case

Description Justen Hyde 2011-08-24 11:52:16 UTC
Created attachment 179 [details]
test case

A simple web application will consume ever increasing amounts of memory when run with sessions enabled. This appears to be related to CacheItems being created rapidly without being garbage collected. Behaviour seen with both boehm and sgen collection.
See also http://go-mono.com/forums/#nabble-td3264509

Running on mono 2.10.2, observed with both mvc2 (test case attached) and mvc3.

To reproduce: Run the test case, point a couple of browser tabs at it and monitor memory usage over time. This will take a while to become evident (since CacheItems are small).

I suspect this is a cause of the mysterious problems that mono sites under high load experience, referred to here http://www.mono-project.com/Mod_mono#Under_high_load.2C_mono_process_consumes_a_lot_of_memory.2C_website_stops_responding
Comment 1 Jeffrey Stedfast 2011-08-24 12:30:29 UTC
Any chance you could test this on 2.10.4?
Comment 2 Justen Hyde 2011-08-25 16:37:42 UTC
I see exactly the same behaviour on 2.10.4
Comment 4 Jeffrey Stedfast 2011-09-29 12:24:44 UTC
Thanks Justin, looks like some of our devs are now able to reproduce. Hopefully they can find & fix the issue :-)
Comment 6 Miguel de Icaza [MSFT] 2011-12-08 09:43:11 UTC
Gonzalo, was this patch backported to 2-10?

This was on 2011-09-29/2011-09-30
Comment 7 Gonzalo Paniagua Javier 2011-12-08 16:27:31 UTC
commit fc7ffed3acfa9f81943350c846cd393b472cfda0
Author: Gonzalo Paniagua Javier <gonzalo.mono@gmail.com>
Date:   Fri Sep 30 01:20:29 2011 -0400

    Plug an unmanaged memory leak
        If Flush() was called with final_flush set to true, we were leaking the
        unmanaged memory in the output stream (if allocated).
        It was "only" an AS leak.
        Thanks to Kumpera for the help debugging this.
Comment 8 Gonzalo Paniagua Javier 2011-12-08 16:32:20 UTC
...but that leak has nothing to do with sessions or caching
Comment 9 Dan Parnham 2012-01-20 07:56:41 UTC
We're still seeing this issue with mono 2.10.8 unfortunately.

My test case uses the MVC3 and if I set:
<sessionState mode="Off" />
in the web.config it does not leak at all (running through XSP).

I discovered something new today when playing with the SessionState attribute, since we may want to selectively enable/disable session state in our application.

In my test case I set:
    <sessionState mode="InProc" cookieless="false" timeout="20" />
and then used:
in the controller.

This should have the same result as setting the session state mode to off in the web.config, but only applied to specific controllers. However, I still saw leaking of the same magnitude as before! 

Hopefully this information may be useful.
Comment 10 Dan Parnham 2012-01-20 08:36:31 UTC
Update to my previous comment:

Having checked a few things I found that I could still write values into the session in my controller, whereas it should be throwing an exception. It seems that the controller SessionState is not being applied in mono (is this another bug?).

The leak is still happening though and we have been working on the next version of our application in the hope that this problem will be addressed before we have to ship.
Comment 11 pgentoo 2012-10-02 02:51:26 UTC
Any status on this issue?  I also see significant leaks in my application, requiring several restarts per day to keep things running smoothly.  :(
Comment 12 Dan Parnham 2012-10-02 06:38:34 UTC
We gave up hoping that this would be fixed and instead looked for an alternative way of doing things. Since we had javascript heavy single-page application it made sense to switch to a REST service based system running in a console (mono + ubuntu).

We now use the awesome ServiceStack (http://www.servicestack.net/) running as a daemon (https://github.com/ServiceStack/ServiceStack/wiki/Run-ServiceStack-as-a-daemon-on-Linux).

I wrote a post about our problems and solutions at http://blog.teadriven.me.uk/2012/03/time-for-rest.html
Comment 13 Michael Thwaite 2014-09-11 06:46:37 UTC
After a week of trying different web server configurations, XSP, etc., to deal with sessions not being properly ended and hence over-running available memory, I was able to stem the leak significantly, if not 100% by clearing out session variables each time a new session started with this code at the end of the Session_Start event. It more than easily extends web server life from hours to weeks:

			try {
				if (HttpContext.Current.Cache ["Sessions"] == null)
					HttpContext.Current.Cache ["Sessions"] = new List<HttpSessionState> ();

				List<HttpSessionState> Sessions = (List<HttpSessionState>)HttpContext.Current.Cache ["Sessions"];

				List<HttpSessionState> SessionsToGo = new List<HttpSessionState> ();

				DateTime oldSession = DateTime.Now.AddMinutes (c.SessionTimeoutMins); // = -30

				#if (DEBUG)
				oldSession = DateTime.Now.AddMinutes (-1); //for testing

				//Kill old sessions
				Sessions.ForEach (session => {

					if (session != null) {

						if (session ["LastAccess"] != null) {
							DateTime LastAccess = (DateTime)session ["LastAccess"];

							if (LastAccess > DateTime.MinValue && LastAccess < oldSession) {

								//Add reference to SessionsToGo
								SessionsToGo.Add (session);

								session.Clear (); //die

								session.Abandon (); //die - this doesn't seem to work though

				//Remove from collection
				SessionsToGo.ForEach (session => Sessions.Remove (session));

				SessionsToGo = null; //hint to the GC!

				//add this session
				Sessions.Add (HttpContext.Current.Session);

				LogEntry.Write ("Session count:" + Sessions.Count.ToString (), 1019);
			} catch (Exception ex) {
				LogEntry.Write ("Session count/manager suffered an error: " + ex.InnerException.Message, 1019, System.Diagnostics.EventLogEntryType.Warning);