C# StreamWriter 和 StreamReader 内存管理问题,为什么使用的内存不会释放?

发布于 2024-09-06 06:50:52 字数 491 浏览 14 评论 0原文

因此,我使用的是 StreamReader,它使用 MemoryStream 写入 StreamWriter 并在此应用程序内部,但内存使用量增加了 300mb(来自较大的输入之一),并且在我使用完它后不会释放:

StreamWriter log = new StreamWriter("tempFile.txt");
log.Write(reader.ReadToEnd());
log.Close();

reader.DiscardBufferedData();
reader.Close();
reader.Dispose();
memoryStream.Dispose();
log.Dispose();
GC.Collect();

在此之前和右侧在我得到 RAM 使用情况之后,之前比之后少了 300 mb,但我不知道为什么。我已经做了我能想到的一切来释放内存,考虑到这里唯一发生的事情是将来自阅读器的数据放置在文本文件中,我不明白为什么需要使用大量内存暂时地。我缺少什么吗?...谢谢。

So I'm using a StreamReader that uses a MemoryStream to write to a StreamWriter and inside of this application but the memory usage increases by 300mb (From one of the larger inputs) and does not deallocate after Im done using it:

StreamWriter log = new StreamWriter("tempFile.txt");
log.Write(reader.ReadToEnd());
log.Close();

reader.DiscardBufferedData();
reader.Close();
reader.Dispose();
memoryStream.Dispose();
log.Dispose();
GC.Collect();

Before this and right after I get the RAM usage and before it is 300 mb less than after but I don't know why. I have done everything I can think of to release that memory considering that the only thing going on here is the data from the reader is being placed in the text file I don't see why any large amount of memory would even need to be used temporarily. Is there something I am missing?... Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

满地尘埃落定 2024-09-13 06:50:52

您是否正在查看进程本身使用的 RAM?我没想到会下降。该进程将保留该内存并重用它以进行进一步的分配。它不会将其交还给操作系统。这正是 .NET 的工作方式。

可能可以通过某些 CLR API 调用让它丢弃内存 - 但通常我不会。

这并不意味着内存确实泄漏了——它仍然可以被同一进程使用。例如,如果您再次执行相同的操作,则可能不需要增加堆的大小 - 您会看到内存使用量在进程级别保持不变。使用 CLR 性能监视器图查看托管堆中使用的内存,以查看是否存在真正泄漏。

(还有各种不同的衡量应用程序实际使用多少内存的方法。哪一个有趣取决于您想要做什么。)

编辑:如果您的代码片段真正表明您正在使用的代码,那么有一个更简单的选择:流式传输数据。

using (TextWriter writer = File.CreateText("tempFile.txt"))
{
    CopyText(reader, writer);
}

static void CopyText(TextReader reader, TextWriter writer)
{
    char[] buffer = new char[8192];
    int charsRead;
    while ((charsRead = reader.Read(buffer, 0, buffer.Length)) > 0)
    {
        writer.Write(buffer, 0, charsRead);
    }
}

请注意,除非您实际上要更改编码,否则您可以在不使用 TextWriter/TextReader 对的情况下执行此操作。

这样,您就不需要将整个字符串及其二进制表示形式存储在内存中(在 MemoryStream 中)。当然,您仍然会使用二进制表示形式 - 您是否必须将所有内容写入 MemoryStream 中?

Are you looking at the RAM used by the process itself? I wouldn't expect that to go down. The process will hang on to that memory and reuse it for further allocations. It doesn't hand it back to the operating system. That's just the way .NET tends to work.

You can potentially get it to throw away the memory with some CLR API call - but usually I wouldn't.

This doesn't mean the memory has really leaked - it's still usable by the same process. For example, if you performed the same action again it probably wouldn't need to increase the size of the heap - you'd see the memory usage stay flat at the process level. Use the CLR perfmon graphs to see the memory used within the managed heap to see if there's a genuine leak.

(There are also various different measure of how much memory an application is actually using. Which one is interesting depends on what you're trying to do.)

EDIT: If your code snippet is genuinely indicative of the code you're using, then there's a much simpler alternative: stream the data.

using (TextWriter writer = File.CreateText("tempFile.txt"))
{
    CopyText(reader, writer);
}

static void CopyText(TextReader reader, TextWriter writer)
{
    char[] buffer = new char[8192];
    int charsRead;
    while ((charsRead = reader.Read(buffer, 0, buffer.Length)) > 0)
    {
        writer.Write(buffer, 0, charsRead);
    }
}

Note that unless you're actually changing the encoding, you could do this without using a TextWriter/TextReader pair to start with.

That way you won't need to have the whole string in memory as well as its binary representation (in the MemoryStream). You'll still have the binary representation of course - do you have to write everything into the MemoryStream to start with?

摇划花蜜的午后 2024-09-13 06:50:52

除了 Jon 发布的内容之外,.NET 运行时的行为方式实际上还为您带来了一些重要的好处 - 保留为进程分配的内存是一件好事,除非系统内存不足(在这种情况下) ,运行时可能会释放它)。

例如,如果您经常需要分配大量内存(如您在此处发布的方法中所示),那么如果进程已经分配了内存,那么效率会更高(即使它不用于存储任何 .NET 对象) 。当你下次运行他的方法时,分配会快得多!

无论如何,您可以做的一些事情是:

  • 您可以尝试运行另一个内存密集型应用程序,以查看运行时是否在系统需要时释放内存
  • 您可以使用一些 .NET 探查器来查看是否存在您期望的活动对象运行该方法后收集

In addition to what Jon posted, the way the .NET runtime behaves acutally gives you some important benefits - keeping the memory allocated for the process is a good thing unless the system is low on memory (in which case, the runtime would probably release it).

For example, if you often need to allocate lot of memory (as in the method you posted here), then it is more efficient if the process already has the memory allocated (even though it isn't used to store any .NET objects). When you runt he method next time, the allocation will be much faster!

Anyway, a few things you could do are:

  • You may try running another memory intensive application to see whether the runtime releases the memory when the system needs it
  • You can use some .NET profiler to see whether there are any live objects that you would expect to be collected after running the method
溺ぐ爱和你が 2024-09-13 06:50:52

我不确定它是否有帮助,但尝试一些 using 范围:

using (StreamReader reader = new StreamReader(somestream))
using (StreamWriter log = new StreamWriter("tempFile.txt"))
{
    log.Write(reader.ReadToEnd());
}

I'm not sure if it'll help, but try some using scoping:

using (StreamReader reader = new StreamReader(somestream))
using (StreamWriter log = new StreamWriter("tempFile.txt"))
{
    log.Write(reader.ReadToEnd());
}
哥,最终变帅啦 2024-09-13 06:50:52

在上面提到的段中,所有 logreadermemoryStream 仍在范围内,因此无法被垃圾收集。我不知道这些对象上的 Dispose 的实现是什么,但即使在调用 Dispose 后,它们也很可能仍然在内存中保存着大部分数据(因为Dispose通常只关闭文件句柄、非托管内存等,并且不一定删除内部缓冲区)。如果您希望它按预期工作,则必须让所有引用的对象超出范围并变为未引用。

不过,实际上,您不应该担心这一点,除非内存使用给您带来了明显的痛苦。

In the segment mentioned above, all of log, reader, and memoryStream are still in scope and so can't be garbage collected. I don't know what the implmentation of Dispose on those objects is, but it's entirely likely that they're still holding much of the data in memory even after Dispose is called (since Dispose typically only closes filehandles, unmanaged memory, and such, and doesn't necessarily delete internal buffers). If you want this to work as expected, you have to let all of the referenced objects go out of scope and become unreferenced.

Really, though, you shouldn't worry about this unless memory usage is causing you explicit pain.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文