ASP.NET 个应用程序的高内存使用率

High memory usage by ASP.NET applications

我们的一些 ASP.Net 应用程序存在问题。我们的一些应用程序从一开始就要求大量内存作为它们的工作集。

在我们的 2 个 webfarm-servers(每个 4gb RAM)上 运行 多个应用程序。我们有一个稳定的环境,大约有 1.2gb 的可用内存。

然后我们添加一个 MVC5 + WebApi v2 + Entity Framework 应用程序,它立即声称 1+gb 作为工作集内存,而实际上只使用了大约 300mb。这会导致其他应用程序抱怨没有足够的剩余内存。

我们已经尝试设置虚拟内存限制和私有内存限制,但没有任何效果。如果我们将其设置为大约 500mb,应用程序仍会使用或多或少相同的内存量(超过 500)并且似乎不遵守规定的限制。

作为参考,我用一个空的 MVC5 项目(VS2013 模板)对此进行了测试,这已经占用了 300mb 的内存,而只使用了大约 10mb。

将应用设置为 32 位应用似乎对减小工作集的大小有一些影响。

有什么方法可以减少工作集的大小,或者对其大小施加硬性限制吗?

编辑: 在使用 Web Api v2 和 Entity Framework 的项目使用大量内存的情况下,我的 API 控制器如下所示:

namespace Foo.Api
{
public class BarController : ApiController
{
    private FooContext db = new FooContext(); 

    public IQueryable<Bar> GetBar(string bla)
    {
        return db.Bar.Where(f => f.Category.Equals(bla)).OrderBy(f => f.Year);
    }
}

在我能找到的大多数教程(包括来自微软的教程)中都是如此。由于 LINQ 延迟加载,此处使用 using 不起作用。如果我在所有地方添加一个 ToList(未测试),它可能会起作用,但这是否有任何其他影响?

编辑2: 如果我这样做就有效

namespace Foo.Api
{
public class BarController : ApiController
{
    public List<Bar> GetBar(string bla)
    {
        using(FooContext db = new FooContext){
           return db.Bar.Where(f => f.Category.Equals(bla)).OrderBy(f => f.Year).ToList();
        }
    }
}

ToList() 对 api 的性能有任何影响吗? (我知道我不能像使用 IQueryable 那样继续廉价查询)

编辑3: 我注意到它的应用程序的私有工作集非常高。有没有办法限制这个? (不会造成不断的回收)

编辑4: 据我所知,我在每个 API 控制器上都有一个 Dispose。我的前端只是一些简单的 MVC 控制器,但大部分是 .cshtml 和 javascript (angular) 文件。

我们有另一个应用程序,只是普通的 mvc,有两个模型和一些简单的视图(没有数据库,或其他可能泄漏的外部资源),这也消耗高达 4-500mb 的内存。如果我分析它,我看不到任何表明内存泄漏的东西,我确实看到实际只使用了 10 或 20 mb,其余是未分配的非托管内存(但私有内存工作集的一部分,因此由此应用程序且无法被任何其他应用程序使用)。

我的一些应用程序也遇到了类似的问题。通过将一次性数据库资源包装在 using 子句中,我能够通过正确关闭一次性数据库资源来解决问题。

对于 Entity Framework,这意味着确保您始终在每次请求后关闭上下文。应在请求之间处理连接。

using (var db = new MyEFContext())
{
   // Execute queries here
   var query = from u as db.User
               where u.UserId = 1234
               select u.Name;

   // Execute the query.
   return query.ToList();

   // This bracket will dispose the context properly.
}

您可能需要将上下文包装到一个请求缓存您的上下文的服务中,以便在整个请求过程中保持其活动状态,并在完成后将其丢弃。

或者,如果使用为整个控制器提供单一上下文的模式 like in the MSDN examples, make sure you override the Dispose(bool) method, like the example here

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        db.Dispose();
    }
    base.Dispose(disposing);
}

所以你的控制器(从上面看)应该是这样的:

namespace Foo.Api
{
    public class BarController : ApiController
    {
        private FooContext db = new FooContext(); 

        public IQueryable<Bar> GetBar(string bla)
        {
             return db.Bar.Where(f => f.Category.Equals(bla)).OrderBy(f => f.Year);
        }

        // WebApi 2 will call this automatically after each 
        // request. You need this to ensure your context is disposed
        // and the memory it is using is freed when your app does garbage 
        // collection.
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

我看到的行为是应用程序会消耗大量内存,但它可以垃圾收集足够的内存以防止它获得 OutOfMemoryException。这使得问题很难找到,但是配置数据库资源解决了它。其中一个应用程序的 RAM 使用量曾经徘徊在 600 MB 左右,现在徘徊在 75 MB 左右。

但是这个建议不仅仅适用于数据库连接。 任何 class 实现 IDisposable 如果你 运行 进入内存泄漏,应该怀疑。但是既然你提到你正在使用 EntityFramework,那么它很可能是可疑的。

删除所有 Telerik Kendo MVC 引用(dll 等)解决了我们的问题。如果我们 运行 没有应用程序,我们所有的内存问题都会消失,我们会看到正常的内存使用。

基本上:这是一个导致高内存使用的外部库。