yield return - 内存优化

yield return - memory optimization

还有一个关于 yield return

的问题

所以我需要远程执行不同的 SQL 脚本。脚本在 TFS 中,所以我自动从 TFS 获取它们,并且该过程遍历所有文件,读取它们在内存中的内容并将内容发送到远程 SQL 服务器。

到目前为止,该过程完美无缺。但现在一些脚本将包含 bulk inserts 将脚本的大小增加到 500,000 MB 或更多。

所以我构建了代码"thinking",我曾经在内存中读取过文件的内容,但现在我有了第二个想法。

这是我的(过度简化):

    public IEnumerable<SqlScriptSummary> Find(string scriptsPath)
    {
        if (!Directory.Exists(scriptsPath))
        {
            throw new DirectoryNotFoundException(scriptsPath);
        }

        var path = new DirectoryInfo(scriptsPath);

        return path.EnumerateFiles("*.sql", SearchOption.TopDirectoryOnly)
            .Select(x =>
            {
                var script = new SqlScriptSummary
                {
                    Name = x.Name,
                    FullName = x.FullName,
                    Content = File.ReadAllText(x.FullName, Encoding.Default)
                };

                return script;
            });
    }

....

    public void ExecuteScripts(string scriptsPath)
    {
        foreach (var script in Find(scriptsPath))
        {
            _scriptRunner.Run(script.Content);
        }
    }

我的理解是 EnumerateFiles 一次会 yield return 每个文件,所以这就是让我 "think" 加载的原因内存中一次一个文件。

但是...

一旦我迭代它们,在 ExecuteScripts 方法中 script 变量会发生什么 foreach 超出范围后循环?那是处置了吗?还是留在记忆中?

我如何重新设计代码以优化内存消耗,例如强制一次只将脚本的内容加载到内存中

其他问题:

Once that I'm iterating them, what happens with the script variable used in the foreach loop after it goes out of scope? Is that disposed? or does it remain in memory?

如果您的意思是在 ExecuteScripts 方法中 - 没有什么可处理的,除非 SqlScriptSummary 实现 IDisposable,这似乎不太可能。但是,这里有两个不同的东西:

  • script 变量在 foreach 循环后超出范围,不能作为 GC root
  • script 变量引用的每个对象在没有其他对象引用它时将有资格进行垃圾回收...包括下一次迭代的 script

所以是的,基本上应该没问题。您将一次加载一个文件,就 GC 无法收集的对象而言,我看不出内存中一次有多个文件内容的任何原因。 (GC 本身是惰性的,因此不太可能一次 恰好 一个脚本在内存中,但您不必担心那方面的事情,因为您的代码确保它不会同时保留对多个脚本的实时引用。)

您可以测试您是否一次只加载一个脚本的方法是尝试使用大型脚本的大型目录(实际上不执行任何操作)。如果您可以处理的脚本多于您的内存,那很好:)