1

我们需要 Winforms 应用程序从本地文件系统(或网络位置)读取数千个文件并将它们存储在数据库中。

我想知道加载文件的最有效方法是什么?总共可能有许多千兆字节的数据。

File.ReadAllBytes当前正在使用,但应用程序最终会在计算机内存用完时锁定。

当前代码循环遍历包含文件路径的表,用于读取二进制数据:

protected CustomFile ConvertFile(string path)
{
    try
    {
        byte[] file = File.ReadAllBytes(path);
        return new CustomFile { FileValue = file };
    }
    catch
    {
        return null;
    }
}

然后使用 NHibernate 作为 ORM 将数据保存到数据库(SQL Server 2008 R2 或 2012)。

4

2 回答 2

1

首先,让我声明我的知识是 NET 4.0 之前的,所以这些信息可能已经过时了,因为我知道他们将在这方面做出改进。

不要使用File.ReadAllBytes读取大文件(大于 85kb),特别是当您按顺序读取许多文件时。我再说一遍,不要。

使用流和BinaryReader.Read之类的东西来缓冲您的阅读。即使这听起来效率不高,因为您不会通过单个缓冲区对 CPU 进行爆破,但如果您使用 ReadAllBytes 执行此操作,它根本不会像您发现的那样工作。

原因是 ReadAllBytes 读取字节数组中的整个内容。如果该字节数组在 mem 中 > 85Kb(还​​有其他考虑因素,例如数组元素的数量),它将进入大对象堆,这很好,但是 LOH 不会移动内存,也不会对释放的空间进行碎片整理,所以,简化,这可能发生:

  • 读取 1GB 文件,你在 LOH 中有一个 1GB 块,保存文件。(无 GC 循环)
  • 读取 1.5GB 文件,您请求 1.5GB 内存块,它进入 LOH 的末尾,但假设您获得一个 GC 周期,因此您之前使用的 1GB 块被清除,但现在您有一块 2.5GB 内存,前1GB免费。
  • 读取一个 1.6GB 的文件,开始的 1GB 空闲块不起作用,所以分配器走到最后。现在你有一个 4.1GB 的内存块。
  • 重复。

您的内存不足,但您肯定并没有真正使用它,碎片可能会杀死您。如果文件非常大(我认为 Windows 32 位的进程空间是 2GB?),您实际上也可以遇到真正的 OOM 情况。

如果文件没有排序或相互依赖,则可能有几个线程通过使用 BinaryReader 缓冲来读取它们将完成工作。

参考:

http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/learning-memory-management/memory-management-fundamentals

https://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/

于 2013-03-29T22:47:50.910 回答
0

如果你有很多文件,你应该一个接一个地阅读它们。

如果您有大文件,并且数据库允许,您应该将它们逐块读取到缓冲区中,然后将它们逐块写入数据库。如果使用,当文件太大而无法放入运行时的内存时File.ReadAllBytes,您可能会得到一个。OutOfMemoryException上限小于2 GiB,应用运行一段时间后内存碎片化时甚至更小。

于 2013-03-29T22:45:19.200 回答