【问题标题】:High memory usage by ASP.NET applicationsASP.NET 应用程序的高内存使用率
【发布时间】:2015-04-02 19:04:01
【问题描述】:

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

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

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

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

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

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

有什么方法可以减小工作集的大小,或者对它的大小进行硬性限制?

编辑: 在使用 Web Api v2 和实体框架的项目使用大量内存的情况下,我的 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: 据我所知,我在每个 APIController 上都有一个 Dispose。我的前端只是一些简单的 MVC 控制器,但大部分是 .cshtml 和 javascript (angular) 文件。

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

【问题讨论】:

  • 你还打算用那块内存做什么?如果它是您的 Web 服务器,它应该用于您的 Web 应用程序。它可能有多种原因——测试它是否真的是你的应用程序,在 Amazon EC2 实例上启动一个新的 Hello World 应用程序,如果它发生在那里,那么你知道它是框架而不是你。如果是你,那么你需要分析你的应用程序,看看你是不是 1) 泄漏内存,或者 2) 进行了太多的长期分配。
  • 通过声明该内存,它使其他应用程序(在同一服务器上)无法使用它。还有一个使用不同内存的应用程序抱怨,因为所有内存都被占用了。我们只是想限制它可以占用多少虚拟内存,以便其他应用程序在需要时可以使用更多内存。
  • 其他应用程序是指另一个网络应用程序吗?或者是其他东西?如果是别的东西,它不需要在那个服务器上,那是你的网络服务器。如果是通过 IIS 的另一个 Web 应用程序;那么我的第一组建议是关键,分析你的代码,确保它不是你的代码(很可能是)。
  • 这是另一个通过 IIS 的 Web 应用程序。我们正在分析,我们可以看到大量(如 700mb+)只是为未使用的 .net 保留的内存。我稍后会尝试添加屏幕截图。
  • 大多数人误解了记忆图...它们通常并不意味着您认为它们会做什么。应用程序会保留大量内存,但如果未提交,则该内存实际上并未使用。此外,很难真正掌握应用程序使用了多少内存,因为内存通常在进程之间共享,例如 .NET 框架本身。所有应用程序共享一个 DLL 的公共实例。

标签: c# asp.net asp.net-mvc iis asp.net-web-api


【解决方案1】:

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

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

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 使用具有单个上下文的模式,请确保覆盖 Dispose(bool) 方法,例如示例 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 左右。

但是这个建议不仅仅适用于数据库连接。如果您遇到内存泄漏,任何实现IDisposable 类都应该受到怀疑。但既然你提到你正在使用 EntityFramework,它最有可能是可疑的。

【讨论】:

  • 关于如何将它用于 Web Api v2 的任何想法?由于 linqs 延迟加载,这不会按原样工作。
  • @NightOwl888 你能详细说明自定义IControllFactory 吗?此外,IoC 模式是否是一种可能的解决方案来保持对这些对象的处置控制? (例如,让 IoC 将服务/存储库甚至特定的 DbContext 注入控制器。通过这样做,IoC 控制了处置)?只是一个想法......
  • @YvesSchelpe - 如果您使用的容器能够连接每个请求对象并且具有正确处理它的机制,那么使用 DI 将有助于解决这个问题。并非所有人都这样做。恕我直言,修复内存泄漏不是开始使用 DI 的正当理由,但如果您使用 DI,您仍然必须确保正确处理非托管资源(例如数据库连接和文件流),这正是您确保这种转变的方式到应用程序的不同部分。
  • @NightOwl888 当然,使用 DI 修复内存泄漏不是预期的方式。这不是我的意思,更多的是 KWyckmans 在上面问你的问题的心态......首先,我知道 StructureMap 和 AutoFac 容器确实处理对象的处理,当然前提是您正确设置它。无论如何,正如我所说,我们不能让它适应 KWyckmans 要求你的场景。
  • 我想我在我使用的每个 api 控制器上都实现了这个,但我必须重新检查。感谢您提供更多信息。
【解决方案2】:

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

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

【讨论】:

  • 你是怎么知道的?有没有办法显示每个库的内存使用情况?
猜你喜欢
  • 1970-01-01
  • 2011-07-19
  • 1970-01-01
  • 2016-10-05
  • 2012-03-28
  • 2013-10-27
  • 1970-01-01
  • 2016-06-02
  • 2011-05-15
相关资源
最近更新 更多