I have a brand new ASP.Net MVC project that is eating up memory and refusing to release it, for reasons I'm unable to fathom.
The Application
The site has just 2 pages, both served by the HomeController.
public class HomeController: Controller
{
public ActionResult Index()
{
GC.Collect();
return View();
}
public ActionResult List()
{
GC.Collect();
var homeViewModel = new HomeViewModel();
for (int i = 0; i < 300; i++)
{
homeViewModel.MyList.Add(new MyClass
{
Field1 = "AAAAAAAAAAAAAAAAAAAAAAAAAAA",
Field2 = "BBBBBBBBBBBBBBBBBBBBBBBBBBB",
Field3 = "CCCCCCCCCCCCCCCCCCCCCCCCCCC",
Field4 = "DDDDDDDDDDDDDDDDDDDDDDDDDDD",
Field5 = "EEEEEEEEEEEEEEEEEEEEEEEEEEE",
Field6 = "FFFFFFFFFFFFFFFFFFFFFFFFFFF"
});
}
return View(homeViewModel);
}
}
public class HomeViewModel
{
public List<MyClass> MyList { get; set; }
public HomeViewModel()
{
MyList = new List<MyClass>();
}
}
public class MyClass
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public string Field4 { get; set; }
public string Field5 { get; set; }
public string Field6 { get; set; }
}
You'll notice that both the Index and List actions force a garbage collection at the beginning of each call.
The Index page simply contains the shared _Layout.cshtml and no other content.
@model WebApplication1.Controllers.HomeViewModel
@{
ViewBag.Title = "Home Page";
}
The List page contains a table of 300 rows, each row showing 6 string properties of a class designed to demonstrate my issue.
@model WebApplication1.Controllers.HomeViewModel
@{
ViewBag.Title = "List Page";
}
<table class="table table-condensed">
@foreach (var item in Model.MyList)
{
<tr>
<td style="@MyHelpers.DetermineStrikethruStyle(true)">@item.Field1</td>
<td style="@MyHelpers.DetermineStrikethruStyle(true)">@item.Field2</td>
<td style="@MyHelpers.DetermineStrikethruStyle(true)">@item.Field3</td>
<td style="@MyHelpers.DetermineStrikethruStyle(true)">@item.Field4</td>
<td style="@MyHelpers.DetermineStrikethruStyle(true)">@item.Field5</td>
<td style="@MyHelpers.DetermineStrikethruStyle(true)">@item.Field6</td>
</tr>
}
</table>
As you can see, the cells in the table are styled based on the result from a Razor HTML Helper, as follows:
@helper DetermineStrikethruStyle(bool isStrikeThru)
{
@(isStrikeThru ? "text-decoration: line-through;" : "")
}
The Problem
Below you can see how the memory is being eaten up each time the List page is refreshed.
On launch – Display Home Page
After Refreshing Index Page x 10
After Displaying List Page for the 1st Time
After Displaying List Page for the 2nd Time
After Displaying List Page for the 3rd Time
After Displaying List Page for the 4th Time
After Displaying List Page for the 5th Time
Avoiding the Problem
By simply removing the calls to the Razor HTML Helper within the List page avoids the issue entirely.
@model WebApplication1.Controllers.HomeViewModel
@{
ViewBag.Title = "List Page";
}
<table class="table table-condensed">
@foreach (var item in Model.MyList)
{
<tr>
<td>@item.Field1</td>
<td>@item.Field2</td>
<td>@item.Field3</td>
<td>@item.Field4</td>
<td>@item.Field5</td>
<td>@item.Field6</td>
</tr>
}
</table>
On launch – Display Home Page
After Refreshing Index Page x 10
After Displaying List Page for the 1st Time
… after 5th time
… after 25th time
… after 50th time
Any ideas? I'm stumped at the moment, and have an unusable application as a result.
Can anyone offer any guidance?
Move
@helper DetermineStrikethruStyle(bool isStrikeThru)
{
@(isStrikeThru ? "text-decoration: line-through;" : "")
}
on a HtmlHelper Extensions method in server side.
public static string DetermineStrikethruStyle(this HtmlHelper helper, bool isStrikeThru)
{
return isStrikeThru ? "text-decoration: line-through;" : String.Empty;
}