Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/matlab/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
导致内存不足异常的大字符串数组(C#)_C#_Memory_Heap_Out Of Memory - Fatal编程技术网

导致内存不足异常的大字符串数组(C#)

导致内存不足异常的大字符串数组(C#),c#,memory,heap,out-of-memory,C#,Memory,Heap,Out Of Memory,我已经编写了一个c#win表单应用程序,允许用户打开一个日志(文本)文件并查看数据网格中的日志行。记录数据的应用程序格式,以便用户可以过滤、搜索等 我遇到的问题是,当用户打开大于300mb的日志文件时,应用程序抛出内存不足异常 应用程序首先将所有日志行加载到一个字符串数组中,然后循环遍历日志行,将日志条目对象添加到列表中 var allLogLines = File.ReadAllLines(logPath).ToList(); var nonNullLogLines = allLogLines

我已经编写了一个c#win表单应用程序,允许用户打开一个日志(文本)文件并查看数据网格中的日志行。记录数据的应用程序格式,以便用户可以过滤、搜索等

我遇到的问题是,当用户打开大于300mb的日志文件时,应用程序抛出内存不足异常

应用程序首先将所有日志行加载到一个字符串数组中,然后循环遍历日志行,将日志条目对象添加到列表中

var allLogLines = File.ReadAllLines(logPath).ToList();
var nonNullLogLines = allLogLines.Where(l => !string.IsNullOrEmpty(l));

this.ParseLogEntries(nonNullLogLines.ToArray());
这个初始步骤(将日志数据加载到字符串数组中)将占用TaskManager中大约1gb的内存

internal override void ParseLogEntries(string[] logLines)
{
    this.LogEntries = new List<LogEntry>();
    this.LogLinesCount = logLines.Count();

    for (int i = 0; i < this.LogLinesCount; i++)
    {
        int entryStart = this.FindMessageCompartment(logLines, i);
        int entryEnd = this.FindMessageCompartment(logLines, entryStart + 1);
        int entryLength = (entryEnd - entryStart) + 1;

        if (entryStart + entryLength > this.LogLinesCount)
        {
            entryLength = this.LogLinesCount - entryStart;
        }

        var logSection = new string[entryLength];

        Array.Copy(logLines, entryStart, logSection, 0, entryLength);
        Array.Clear(logLines, i, entryLength - 1);

        this.AddLogEntry(logSection);

        i = (entryEnd - 1);
    }
}
内部重写无效ParseLogEntries(字符串[]日志行)
{
this.LogEntries=新列表();
this.loglinescont=logLines.Count();
对于(int i=0;ithis.loglineScont)
{
entryLength=this.loglinescont-entryStart;
}
var logSection=新字符串[entryLength];
复制(logLines,entryStart,logSection,0,entryLength);
数组。清除(对数线,i,入口长度-1);
此.AddLogEntry(日志部分);
i=(入口端-1);
}
}
AddLogEntry方法将日志条目添加到列表(LogEntries)。for循环设法解析大约50%的日志文件,然后发生内存不足异常。此时任务管理器报告应用程序正在使用大约1.3gb的内存

正如您在上面看到的,我已经添加了Array.Clear以清空已成功解析的日志数据部分,因此我希望随着对象添加到集合中,大型日志数据数组使用的内存量(首先是1gb)会稳步减少,但不会。事实上,这一行对内存使用没有影响,即使我定期添加一个GC collect

读过LOH之后,我假设这是因为堆没有被压缩,因为大数组的部分被置空,所以它总是使用相同的1gb内存,不管其内容如何

是否有任何方法可以减少数据解析时所占用的内存量,或者进行可能的返工以更好地利用内存?我觉得奇怪的是,一个300mb的文本文件在放入字符串数组时,会消耗1gb的内存


谢谢。

您可以使用解析单行的
ParseLogEntry(string[]logLines)
方法来代替一次性解析所有日志行的方法

如果将这与一次迭代一行日志文件中的行相结合(例如,通过创建自己的一行),那么首先就可以避免创建大数组
string[]logLines

一种方法可能是这样的:

static IEnumerable<string> ReadLines(string filename)
{
    using (TextReader reader = File.OpenText(filename))
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

// And use the function somewhere to parse the log

var logEntries = new List<LogEntry>()
foreach (string line in ReadLines("log.txt"))
{
    logEntries.Add(ParseLogEntry(line));
}
静态IEnumerable可读行(字符串文件名)
{
使用(TextReader=File.OpenText(文件名))
{
弦线;
而((line=reader.ReadLine())!=null)
{
收益率回归线;
}
}
}
//并在某个地方使用该函数来解析日志
var logEntries=新列表()
foreach(ReadLines(“log.txt”)中的字符串行)
{
添加(ParseLogEntry(行));
}

如果您使用的是.NET 4.0或更高版本,您当然可以使用sll在另一个答案中指出的
File.ReadLines
方法,而不是创建自己的方法。

首先我看到的是,您正在通过使用以下语句重用并加倍内存使用:

File.ReadAllLines(logPath).ToList();
系统将首先读取所有行,然后将其转换为一个列表,该列表的使用量将增加一倍

我建议您使用以下方式通过streamreader读入该文件:

使用(var sr=newstreamreader(fileName)){//Get Data out here}

这样,一旦您离开语句,内存就会被释放


另外,Array.Copy将使用更多内存,因此请尝试在Using语句中创建并创建所需的对象,或使对象IDisposable,以便垃圾收集器可以节省时间。

我建议不要将所有文件加载到内存中,而使用延迟读取。对于>=
.NET4
,您可以利用它来读取文件

使用ReadLines时,可以开始枚举 返回整个集合之前的字符串;因此,当你 如果您使用的是非常大的文件,ReadLines的效率会更高


我知道这不会回答你的问题,但是你可能想考虑不完全加载你的文件到内存。< /P> 在您的情况下,日志文件需要300MB的内存,但如果它需要2.5GB呢?
特别是如果结果是在datagrid中显示,您可能希望改用分页,并在每次需要时从文件中加载一小块数据。

字符串需要堆上的连续内存段;当堆上有很多长字符串,并且您试图分配另一个字符串,但没有所需长度的可用段时,应用程序可能会抛出“内存不足”

您的
Array.Clear
行可能没有帮助,因为
logSection
字符串不会被垃圾收集,事实上,随着循环的迭代,运行时将很困难,因为在堆上找到一个例如10K的空间比找到10K的空间更难


这就是你的问题所在。至于解决方案,一般来说,我建议采用更懒惰的解决方案。你真的需要主内存中的所有字符串吗?如果是,为什么不至少从一个
StreamReader
读取,而将所有内容加载到
string[]logLines

什么是
查找消息隔室
?也不要使用数组,使用generic
Lis
foreach (string line in File.ReadLines(@"path-to-a-file"))
{
   // single line processing logic
}