5

我正在尝试在进程中获取可用内存,以确保我不会收到 OutOfMemoryException。我搜索了互联网,发现了几个如何使用但不可的内存的示例。

让我提供用例...

我有一个正在执行批量插入的进程(使用 SqlBulkCopy)。我正在将 a 传递给DataTableWriteToServer方法。我不能使用 aDataReader因为我必须能够在失败时重试该过程。我的第一个想法是一次选择任意数量的行插入,比如 50,000。但这是一个不知道数据的通用过程;它不知道列数,也不知道每行中的数据量。所以我想我可以在向 中添加行时监视内存DataTable,然后在它快SqlBulkCopy耗尽内存时将其发布到。

这是一种有效的方法还是有更好的方法?
如果这是一种有效的方法,我将使用什么函数来确定可用内存量?

到目前为止,这是我的代码......这AvailableMemoryIsLow是我无法弄清楚如何确定的。

// m_buffer is a read-once cache (implements IDataReader) that pulls 
// data from an external source as needed so it uses very little memory.
// My original implementation just used m_buffer as the parameter of 
// WriteToServer but now I have to add retry logic into the process.

DataTable dataTable = new DataTable(m_tableName);
foreach (DataField d in m_buffer.GetColumns())
    dataTable.Columns.Add(new DataColumn(d.FieldName, d.FieldType));

while (m_buffer.Read())
{
    DataRow row = dataTable.NewRow();
    for (int i = 0; i < m_buffer.FieldCount; i++)
        row[i] = m_buffer.GetValue(i);

    dataTable.Rows.Add(row);

    // How do I determine AvailableMemoryIsLow
    if (rowCount++ >= 50000 || AvailableMemoryIsLow)
    {
        PutDataIntoDatabase(dataTable);
        dataTable.Clear();
        rowCount = 0;
    }
}

if (dataTable.Rows.Count > 0)
    PutDataIntoDatabase(dataTable);
4

3 回答 3

3

显然,您是在 32 位机器上运行此代码,否则您不会遇到此问题。一般来说,推动程序消耗几乎所有可用的虚拟内存空间(2 GB)并不是一个合理的做法。除了永远存在的 OOM 危险之外,您正在处理的数据类型是“实时数据”,它很可能会映射到 RAM。需要几乎所有可用 RAM 的程序对该程序、操作系统和在该机器上运行的其他进程的操作非常不利。

您强制操作系统开始选择如何在进程需要的内存和为文件系统缓存保留的内存之间分配内存。这种选择最终总是迫使数据从 RAM 进入页面文件。这会大大减慢操作速度,无论是在写入时还是在进程需要它返回 RAM 时。称为“抖动”的操作系统性能问题。

只是不要这样做,在 RAM 中存储如此多的数据不会使您的程序更快。它使它变慢。在 32 位操作系统上消耗的 RAM 量的合理上限徘徊在 500 兆字节附近。没有必要完全达到这个限制,计算行数就足够了。

于 2012-12-05T17:07:08.120 回答
1

您提到您找到了告诉您分配了多少内存的方法

    GC.GetTotalMemory(false);

是一种这样的方法(我想你已经找到了)。

我想从 MSDN 文档中指出一件事。

检索当前认为分配的字节数

这是 GC.GetTotalMemory 方法文档的最顶部。我想指出上述短语中的思想一词。现在我知道您知道如何找到问题中提到的分配数量,但是我提出这一点是为了说明 C# 是一种托管语言。内存使用和消耗从您那里抽象出来,甚至 GC 方法也只是为了让您对进程中发生的事情有一个模糊的了解。对我来说,手动处理内存级别听起来很冒险且不可靠。

我建议您使用原始方法,但将批量大小拉回一个水平,这样无论您使用多少列,您都不太可能出现内存不足异常。想想几百,也许几千,而不是几万。即使您尝试检测到这些级别的内存问题风险,您通过较大批次获得的任何性能提升都可能超过。另一个答案中提到的性能工具将是确定批量大小应该是多少以及是否存在问题的好方法。

于 2012-12-05T16:32:35.910 回答
0

问题是有很多不同种类的“资源”。其中任何一个都可以用“OutOfMemoryException”表现出来。

然而,您最好的选择可能是GC.GetTotalMemory(false).

更好的方法是获得像 JetBrains dotTrace或 RedGate ANTS这样的工具。

恕我直言 ...

PS:

如果您正在执行 SQL 批量复制,请务必设置 EnableStreaming:

于 2012-12-05T16:05:55.170 回答