Search code examples
c#asp.netasp.net-mvc-4razorrazor-2

ASP.net MVC - Razor HTML Helper is grabbing memory and not letting go


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

enter image description here

After Refreshing Index Page x 10

enter image description here

After Displaying List Page for the 1st Time

enter image description here

After Displaying List Page for the 2nd Time

enter image description here

After Displaying List Page for the 3rd Time

enter image description here

After Displaying List Page for the 4th Time

enter image description here

After Displaying List Page for the 5th Time

enter image description here

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

enter image description here

After Refreshing Index Page x 10

enter image description here

After Displaying List Page for the 1st Time

enter image description here

… after 5th time

enter image description here

… after 25th time

enter image description here

… after 50th time

enter image description here

Any ideas? I'm stumped at the moment, and have an unusable application as a result.

Can anyone offer any guidance?


Solution

  • 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;
    }