【问题标题】:Measure total amount of RAM available to application测量应用程序可用的 RAM 总量
【发布时间】:2015-12-26 00:03:33
【问题描述】:

我有一个我一直在研究的数据解析应用程序,考虑到它正在读取的文本文件的绝对大小,控制内存使用是获得良好性能的关键。这里的两部分策略首先测量每个文件对总和的贡献有多少 RAM,但它还需要知道在给定时间点应用程序有多少 RAM 可用。如果有足够的 RAM 可用,应用程序会选择在内存中进行处理。否则,它会切换到在磁盘上执行所有或大部分操作的模式。

测量文件对内存使用的贡献既快捷又简单:

    static Int64 GetSizeInMemory(string path)
    {
        //THIS CODE IS SPEEDY
        Int64 r = ((Func<Int64>)(
                    () =>
                        {
                            try
                            {
                                using (Stream s = new MemoryStream())
                                {
                                    BinaryFormatter formatter = new BinaryFormatter();
                                    formatter.Serialize(s, File.ReadAllLines(path));
                                    return s.Length;
                                }
                            }
                            catch
                            {
                                //this file is way too big
                                return -1;
                            }
                        }
                ))();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        return r;
    }

但是,测量可用内存总量既慢又困难。在这种情况下,我试图通过捕获堆栈溢出错误来做到这一点,在我看来,这应该给出最可靠的数字。

    static Int64 GetMaxAllowedMemory()
    {
        //THIS CODE IS SLOW
        Int64 r = ((Func<Int64>)(
                    () =>
                        {
                            byte[] b = new byte[]{};
                            Int64 rs = 0;
                            while (true)
                            {
                                try
                                {
                                    Array.Resize<byte>(ref b, b.Length + 1);
                                    b[b.Length - 1] = new byte();
                                    rs = b.Length;
                                } catch (Exception e) {
                                    break;
                                }
                            }
                            b = null;
                            return rs;
                        }
                ))();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        return r;
    }

我应该在这里使用更好的方法吗?

请注意我在 Stack Overflow 上查看了许多与此类似的问题,但大多数问题仅涉及获取计算机上可用 RAM 总量的数字,这不是与 .NET 进程在运行时允许的最大 RAM 量相同。

更新

收到答复后,我想出了以下方法,可让我获得应用程序可用的 RAM 总量。

    static Int64 GetMemoryFailPoint()
    {
        Int64 r = ((Func<Int64>)(
                    () =>
                    {
                        int rs = 1;
                        while (true)
                        {
                            try
                            {
                                using (new System.Runtime.MemoryFailPoint(rs))
                                {
                                }
                            }
                            catch {
                                break;
                            }
                            rs++;
                        }
                        return Convert.ToInt64(rs) * 1000000;
                    }
                ))();
        return r;
    }

【问题讨论】:

  • 对于您的更新,您可能可以通过将值加倍直到失败然后除以 2 并在该点之后缓慢上升来使您的代码更快。此外,根据the remarks section of the documentation “MemoryFailPoint 以 16 MB 的粒度运行。任何小于 16 MB 的值都被视为 16 MB,其他值被视为 16 MB 的下一个最大倍数” 所以rs++ 应该是 rs += 16,你正在做 15 次额外的检查,将返回相同的结果。
  • 最后,一个 MB 中有 1 &lt;&lt; 20 (1048576) 字节,所以你的回报应该是 return Convert.ToInt64(rs) * (1 &lt;&lt; 20);。对于 2048 MB (2GB),这将是 99,483,648 字节的差异。
  • @ScottChamberlain 谢谢!我一定会考虑的。另外,关于返回的指针是最有帮助的。

标签: c# memory-management


【解决方案1】:

您可以尝试使用MemoryFailPoint 类:

try
{
    using (new System.Runtime.MemoryFailPoint(1024)) // 1024 megabytes
    {
        // Do processing in memory
    }
}
catch (InsufficientMemoryException)
{
    // Do processing on disk
}

基于此original post

【讨论】:

    【解决方案2】:

    而不是将整个文件读入内存并查看它是否失败,您可以使用MemoryFailPoint 通过使用文件的大小来检查是否有足够的内存可用于进行内存处理在磁盘上。

    void ProcessFile(string path)
    {
        try
        {
            var fileInfo = new FileInfo(path);
            var fileSizeInMb = (int)(fileInfo.Length >> 20);
            using (new System.Runtime.MemoryFailPoint(fileSizeInMb))
            {
                // Do processing in memory
            }
        }
        catch (InsufficientMemoryException)
        {
            // Do processing on disk
        }
    }
    

    【讨论】:

    • 很遗憾,这似乎不能准确衡量文件的影响。当我运行我的原始函数来测试文件在内存中的大小时,我得到了 166.6 MB 的大小,使用文件信息我得到了 ~159 MB。这减少了 7.7 MB 的影响。将文件大小与将文件读入内存时对 RAM 的测量影响进行比较会很容易。
    • 没关系。 MemoryFailPoint 实际上允许在堆栈溢出错误发生之前确定可用的内存总量。但是,用文件大小来衡量是不合适的,因为内存中的大小大于磁盘上的大小。
    • @JoshuaDannemann MemoryFailpoint 的分辨率无论如何都是 16 MB,它向上舍入到下一个 16MB 块(请参阅文档),因此 7 MB 的差异可能不会产生太大的影响。
    • 是否向上取整,关键是文件本身在内存中的大小比在磁盘上的大。因此 fileinfo 返回的数字明显低于内存中的实际大小。 7 MB 的差异,考虑到我的程序内存需求的微薄余量是相当显着的。但是,我可以使用 MemoryFailPoint 通过循环运行它来获取可用内存的总量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-22
    • 2013-09-11
    • 1970-01-01
    • 2015-03-14
    • 2012-02-19
    • 1970-01-01
    相关资源
    最近更新 更多