如果谓词为假,C#SkipWhile会泄漏内存

时间:2016-10-24 21:19:26

标签: c# linq memory-leaks

// C:\logs\AzureSDK.log is ~2.5GB file
IEnumerable<string> lines = File.ReadLines(@"C:\logs\AzureSDK.log").SkipWhile(line => false);

Console.WriteLine(string.Join("\n", lines));
return;

这显然不返回迭代器并在内部分配内存,直到我得到OOM 。在true谓词中返回SkipWhile不会导致这种情况并按预期完成(在执行期间偶然MB内存使用量)

根据文档,方法签名和常识,SkipWhile必须返回迭代器,而不是将所有数据加载到内存中。

机器信息

Microsoft Windows [Version 10.0.14393]
Target 4.5.2, AnyCPU, Release
VS 2015 Update 3
NET 4.6.01586

思考?我必须做一些愚蠢的事情,但不确定是什么

UPD:愚蠢的事情是字符串。我忘记了,这是附加到单个StringBuilder将所有行加载到内存中。

我还检查过SkipWhile来源,这显然非常好:

public static IEnumerable<TSource> SkipWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    return SkipWhileIterator<TSource>(source, predicate);
}

static IEnumerable<TSource> SkipWhileIterator<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    bool yielding = false;
    foreach (TSource element in source) {
        if (!yielding && !predicate(element)) yielding = true;
        if (yielding) yield return element;
    }
}

2 个答案:

答案 0 :(得分:5)

SkipWhile会返回一个枚举器。但是然后你使用string.Join来连接所有内容,因此最终将整个文件加载到内存中。

如果您更改代码以独立处理每一行,您会发现使用的内存要少得多:

foreach (var line in File.ReadLines(@"C:\logs\AzureSDK.log").SkipWhile(_ => false))
{
    Console.WriteLine(line);
}

答案 1 :(得分:3)

您的错误不在SkipWhile上,当您传递true时会导致它跳过每一行 - 没有为您的联接返回任何结果。

string.Join导致内存不足异常,因为它试图分配长度为2.5gb的字符串。