进程的可用内存

时间:2012-12-05 16:02:40

标签: c# c#-4.0

我正在尝试获取进程中的可用内存,以确保我没有得到OutOfMemoryException。我搜索了互联网,发现了几个如何获取内存使用而不是可用的例子。

让我提供用例......

我有一个正在进行批量插入的进程(使用SqlBulkCopy)。我将DataTable传递给WriteToServer方法。我不能使用DataReader,因为我必须能够在失败时重试该过程。我的第一个想法是选择一次插入任意数量的行,比如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);

3 个答案:

答案 0 :(得分:3)

显然,您在32位计算机上运行此代码,否则您将不会遇到此问题。通常,推动程序消耗几乎所有可用的虚拟内存空间(2千兆字节)是不合理的事情。与OOM永远存在的危险相比,您处理的数据类型是“实时数据”,很可能被映射到RAM。一个需要几乎所有可用RAM的程序对该程序,操作系统和在该机器上运行的其他进程的操作非常不利。

您强制操作系统开始选择如何在进程需要的内容之间分配RAM以及为文件系统缓存预留的内容。这种选择总是最终迫使数据从RAM进入分页文件。这可能会大大减慢操作速度,无论是在写入时还是在进程需要在RAM中重新执行时。操作系统性能问题称为“颠簸”。

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

答案 1 :(得分:1)

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

    GC.GetTotalMemory(false);

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

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

  

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

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

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

答案 2 :(得分:0)

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

可能你最好的选择是GC.GetTotalMemory(false)

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

恕我直言......

PS:

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