c# - .NET 堆填满了字符串对象 -> OutOfMemoryException

标签 c# .net out-of-memory heap-memory

我经常(每 30-60 分钟)在我的 Windows 服务中收到 System.OutOfMemoryException。该服务的工作是遍历 6 个目录,其中包含服务数据清洗为通用 XML 数据格式的数据文件。

这 6 个文件夹每个包含 5-10.000 个文件,因此文件总数约为 45.000,并且每天都会添加新文件。每天大约添加 1-2000 个新文件。文件大小在 4KB 到 500KB 之间。

每个数据文件通过XElement对象被清洗成通用的XML数据格式。

我在服务上使用了 RedGates ANTS Memory Profiler,使用最多内存的对象是字符串(大约 90.000.000 字节)和 XElement(大约 51.000.000 字节)。

在 Memory Profiler 中,当我跟踪什么在使用字符串对象时,我可以看到大部分 (93%) XElement 对象正在使用字符串对象。

服务器有 6 个 CPU 和 6GB RAM,所以我不明白为什么我会收到 OutOfMemoryException。如果我查看进程中的 Windows 服务,它最大使用 RAM 是 1.2GB。

我读到 .NET 垃圾收集器不清除字符串对象,因为字符串对象存储在内部表中。这可能是错误吗?如果是,我该怎么办?

下面的代码显示了我是如何遍历文件的。如您所见,我也曾尝试一次获取 20 个文件。这只会将 OutOfMemoryException 推送几个小时,因此该服务将运行 4-5 小时而不是 30-60 分钟。

为什么我会出现 OutOfMemoryException?

private static void CheckExistingImportFiles(object sender, System.Timers.ElapsedEventArgs e)
    {
        CheckTimer.Stop();
        var dir = Directory.GetFiles(RawDataDirectory.FullName, "*.*", SearchOption.AllDirectories);

        List<ManualResetEvent> doneEvents = new List<ManualResetEvent>();
        int i = 0;
        //int doNumberOfFiles = 20;

        foreach (string existingFile in Directory.GetFiles(RawDataDirectory.FullName, "*.*", SearchOption.AllDirectories))
        {
            if (existingFile.EndsWith("ignored") || existingFile.EndsWith("error") || existingFile.EndsWith("importing"))
            {
                //if (DateTime.UtcNow.Subtract(File.GetCreationTimeUtc(existingFile)).TotalDays > 5)
                //  File.Delete(existingFile);
                //continue;
            }

            StringBuilder fullFileName = new StringBuilder().Append(existingFile);

            if (!fullFileName.ToString().ToLower().EndsWith("error") && !fullFileName.ToString().ToLower().EndsWith("ignored") && !fullFileName.ToString().ToLower().EndsWith("importing"))
            {
                File.Move(fullFileName.ToString(), fullFileName + ".importing");
                fullFileName = fullFileName.Append(".importing");

                ImportFileJob newJob = new ImportFileJob(fullFileName.ToString());

                doneEvents.Add(new ManualResetEvent(false));

                ThreadPool.QueueUserWorkItem(newJob.Run, doneEvents.ElementAt(i));
                i++;
            }

            //if (i > doNumberOfFiles)
            //{
            //    i = 0;
            //    doNumberOfFiles = 20;
            //    break;
            //}
        }
        i = 0;
        WaitHandle.WaitAll(doneEvents.ToArray());

        CheckTimer.Start();
    }

最佳答案

Directory.GetFiles(RawDataDirectory.FullName, "*.*", SearchOption.AllDirectories);

这将返回一个数组。如果目录中的文件与您声明的一样多,那么这些文件将是非常大的数组,大到足以放置在大对象堆中。那里的多个大型数组很容易导致 OutOfMemoryException。以下行没有帮助

var dir = Directory.GetFiles(RawDataDirectory.FullName, "*.*", SearchOption.AllDirectories);

有一个什么都不做的变量“dir”。每次方法执行都会创建两次大数组。

关于c# - .NET 堆填满了字符串对象 -> OutOfMemoryException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9348651/

相关文章:

c# - 将文件写入 Web 服务器 - ASP.NET

javascript - 使用 Javascript 获取 Gridview 行中特定项目的值

c# - 在 Listbox 上使用自定义 DataTemplate 时,SelectedItem 绑定(bind)停止工作

c# - SignalR Core 如何每 n 秒向客户端发送消息

asp.net - 在 Web 配置中添加服务器运行时标记会导致 500.19 错误

android ndk,如何将图像文件读取到位图jobject?

c# - MimeKit:如何嵌入图像?

.net - MSBuild 和 F# 的 FAKE 有什么区别?

c# - 处理大字符串,这是大对象堆碎片吗?

c# - .NET 内存不足异常 - 使用 1.3GB 但安装了 16GB