我一直在开发一个数据解析应用程序,考虑到它正在读取的文本文件的绝对大小,控制内存使用是获得良好性能的关键。这里的两部分策略首先测量每个文件将为总数贡献多少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;
}
我应该在这里使用更好的方法吗?
请注意我看过许多类似于堆栈溢出的问题,但大多数只处理获得计算机上可用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;
}
您可以尝试使用MemoryFailPoint
类:
try
{
using (new System.Runtime.MemoryFailPoint(1024)) // 1024 megabytes
{
// Do processing in memory
}
}
catch (InsufficientMemoryException)
{
// Do processing on disk
}
根据原文改编
与其将整个文件读入内存并查看它是否失败,不如使用MemoryFailPoint
来检查是否有足够的ram可用,通过使用磁盘上文件的大小来进行内存中的处理。
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
}
}