【问题标题】:Exception of type 'System.OutOfMemoryException' was thrown. Why?引发了“System.OutOfMemoryException”类型的异常。为什么?
【发布时间】:2010-09-26 06:41:37
【问题描述】:

我有一个返回大约 590,000 条记录的动态查询。它第一次运行成功,但如果我再次运行它,我会不断收到System.OutOfMemoryException。发生这种情况的一些原因是什么?

错误发生在这里:

  public static DataSet GetDataSet(string databaseName,string
                                   storedProcedureName,params object[] parameters)
    {
        //Creates blank dataset
        DataSet ds = null;

        try
        {
            //Creates database
            Database db = DatabaseFactory.CreateDatabase(databaseName);
            //Creates command to execute
            DbCommand dbCommand = db.GetStoredProcCommand(storedProcedureName);
            dbCommand.CommandTimeout = COMMAND_TIMEOUT;
            //Returns the list of SQL parameters associated with that stored proecdure
            db.DiscoverParameters(dbCommand);

            int i = 1;
            //Loop through the list of parameters and set the values
            foreach (object parameter in parameters)
            {
                dbCommand.Parameters[i++].Value = parameter;
            }
            //Retrieve dataset and set to ds
            ds = db.ExecuteDataSet(dbCommand);
        }
            //Check for exceptions
        catch (SqlException sqle)
        {
            throw sqle;
        }
        catch (Exception e)
        {
            throw e; // Error is thrown here.
        }
        //Returns dataset
        return ds;
    }

这是在按钮单击时运行的代码:

protected void btnSearchSBIDatabase_Click(object sender, EventArgs e)
{

        LicenseSearch ls = new LicenseSearch();

        DataTable dtSearchResults = new DataTable();

        dtSearchResults = ls.Search();

        Session["dtSearchResults"] = dtSearchResults;

        Response.Redirect("~/FCCSearch/SearchResults.aspx");
        }
        else
            lblResults.Visible = true;
    }

【问题讨论】:

  • 590K 行有点过分,你不觉得吗?
  • 问题不只是你将DataTable存储在Session中,然后在重新查询时,你的Session变量和原始DataTable都在内存中吗?顺便说一句,不要调用throw e;,调用throw;,否则你的堆栈跟踪会说catch 处理程序生成了异常,而实际上它是包含代码。

标签: asp.net sql-server tsql


【解决方案1】:

尝试尽可能多地破坏您的大数据,因为我已经多次遇到此类问题。其中我有超过 100 万条记录,有 15 列。

【讨论】:

    【解决方案2】:

    哪里失败了?

    我同意您的问题可能是您的 600,000 行数据集可能太大了。我看到您随后将其添加到 Session.如果您使用的是 Sql 会话状态,它也必须序列化该数据。

    即使您正确处理对象,如果您运行两次(一次在会话中,一次在程序代码中),您将始终在内存中至少有 2 个此数据集的副本。这永远不会在 Web 应用程序中扩展。

    算一下,600,000 行,即使每行 1-128 位 guid 也会产生 9.6 兆字节 (600k * 128 / 8) 的数据,更不用说数据集开销了。

    减少你的结果。

    【讨论】:

      【解决方案3】:

      第一次运行成功, 但如果我再次运行它,我会不断得到 一个 System.OutOfMemoryException。什么 这可能是一些原因 发生了什么?

      不管其他人怎么说,该错误与忘记处置您的 DBCommand 或 DBConnection 无关,您不会通过处置其中任何一个来解决您的错误。

      该错误与包含近 600,000 行数据的数据集有关。显然,您的数据集消耗了您机器上 50% 以上的可用内存。显然,当您在第一个数据集被垃圾收集之前返回另一个相同大小的数据集时,您将耗尽内存。就这么简单。

      您可以通过以下几种方式解决此问题:

      • 考虑返回更少的记录。我个人无法想象返回 600K 记录对用户有用的时候。要最小化返回的记录,请尝试:

        • 将查询限制为前 1000 条记录。如果查询返回的结果超过 1000 条,请通知用户缩小搜索范围。

        • 如果您的用户真的坚持要一次查看这么多数据,请尝试分页数据。请记住:Google 不会一次向您显示全部 22 亿条搜索结果,它一次向您显示 20 条左右的记录。 Google 可能不会一次将所有 22 亿条结果保存在内存中,它可能发现重新查询其数据库以生成新页面时内存效率更高。

      • 如果您只需要遍历数据并且不需要随机访问,请尝试返回数据读取器。数据读取器一次只能将一条记录加载到内存中。

      如果这些都不是一个选项,那么您需要强制 .NET 在使用以下方法之一调用您的方法之前释放数据集使用的内存:

      • 删除对旧数据集的所有引用。任何对数据集的引用都会阻止它被内存回收。

      • 如果您不能对数据集的所有引用都清空,请清除数据集中的所有行以及绑定到这些行的所有对象。这将删除对数据行的引用并允许它们被垃圾收集器吃掉。

      我认为您不需要调用 GC.Collect() 来强制生成循环。调用GC.Collect() 不仅通常是个坏主意,因为足够的内存压力会导致 .NET 自行调用垃圾收集器。

      注意:对数据集调用 Dispose 不会释放任何内存,也不会调用垃圾收集器,也不会删除对数据集的引用。 Dispose 用于清理非托管资源,但 DataSet 没有任何非托管资源。它只实现了 IDispoable,因为它是 MarshalByValueComponent 固有的,因此数据集上的 Dispose 方法几乎没有用。

      【讨论】:

      • 您好,如果用户没有选择任何额外的条件并运行查询,则返回准确的 754600 行。我确实打开了分页,但所有结果仍然返回到数据集中。
      • 我遇到了这个问题,我发现每次用户想要查看新页面时重新查询数据库效率更高。它需要更多代码,但没有其他解决方法。使用此代码帮助您在 SQL 中对结果进行分页:davidhayden.com/blog/dave/archive/2005/12/30/2652.aspx
      【解决方案4】:

      你显然不是在处理东西。

      在临时使用实现 IDisposable 的对象时考虑“使用”命令。

      【讨论】:

        【解决方案5】:

        也许您没有处理上一次运行的连接/结果类,这意味着它们仍然在内存中徘徊。

        【讨论】:

        • 您可以使用各种内存配置文件...这是我遇到的最好的配置文件:memprofiler.com
        • 在您刚刚发布的示例中,确保命令和连接已关闭/处置。
        • 谢谢,我要试一试。我以前从未使用过内存分析器。
        • 您指的是 DbCommand,对吧?我会关闭并丢弃样本的哪一部分?我也在使用企业库,这会自动关闭连接吗?
        • 每次执行上述代码时,DbCommand 始终为空。这不是说它会自动关闭吗?
        猜你喜欢
        • 1970-01-01
        • 2013-01-14
        • 2013-08-28
        • 2015-07-18
        • 2011-02-14
        相关资源
        最近更新 更多