File.ReadAllText 导致内存泄漏
我有一个应用程序跟踪日志文件。每次日志文件更新(通常是连续的一系列更新)时,内存使用就会失控。
我已经找到了这个调用的问题:
if (File.Exists(Path + "\\logfile.txt"))
Data = File.ReadAllText(Path + "\\logfile.txt");
这是从 LoadAllData 内部调用的,在这里。
private void FileChangeNotificationHandler(object source, FileSystemEventArgs e)
{
this.Dispatcher.BeginInvoke
(new Action(delegate()
{
Logfile.GetPath();
Logfile.LoadAllData();
LogText.Clear();
LogText.Text = Logfile.Data;
if (CheckFollowTail.IsChecked == true) LogText.ScrollToEnd();
}));
}
有谁知道为什么会发生这种情况?我认为它与委托或处理程序有关。
Possible Duplicate:
TextBox.Text Leaking Memory in WPF Application
I've got an application trailing a logfile. Every time the logfile updates (which is usually a series of updates in a row) the memory use balloons out of control.
I've tracked down the problem to this call:
if (File.Exists(Path + "\\logfile.txt"))
Data = File.ReadAllText(Path + "\\logfile.txt");
This is being called from within LoadAllData, here.
private void FileChangeNotificationHandler(object source, FileSystemEventArgs e)
{
this.Dispatcher.BeginInvoke
(new Action(delegate()
{
Logfile.GetPath();
Logfile.LoadAllData();
LogText.Clear();
LogText.Text = Logfile.Data;
if (CheckFollowTail.IsChecked == true) LogText.ScrollToEnd();
}));
}
Does anyone have insight on why this occurring? I assume it's related to the delegate or the handler.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这可能取决于您将日志文件数据加载到内存中的数量和频率。
GC 需要时间,因此如果您快速连续地重复此操作,那么在下一次 GC 之前,内存中可能会有多个文件的数据。这看起来效率很低。您应该考虑使用基于流的阅读器,以避免将所有数据保留在内存中。如果您确实使用流读取器,请确保事后
处置
它,以避免引入另一次泄漏。另一件要检查的事情是您没有在某处订阅静态事件,从而防止您的对象树被处置。它是一个网络应用程序吗?
It's probably just down to the amount and frequency with which you are loading log file data into memory.
GC takes time, so it you are repeating this in quick succession, then chances are you'll have several files worth of data in memory until the next GC. This seems very inefficient. You should consider the use of a stream based reader, to avoid keeping all the data in memory. If you do use a stream reader, make sure you
dispose
of it afterwards to avoid introducing another leak.The another thing to check it that your not subscribing to a static event somewhere and therefore preventing your object tree from being disposed. Is it a web app?
首先,检查文件是否存在是错误的。这是因为文件系统是易失性的,而且发挥作用的不仅仅是存在(例如权限)。正确的方法是仅打开文件,然后在失败时处理异常。
现在,谈谈你所说的问题。我怀疑正在发生的事情是日志变得足够大以使用大型对象堆(85000 字节就足够了,iirc,并记住.Net 使用 utf16(2 字节)字符)。一个 43K 的 ascii 日志文件就足以引发问题,因为在这个大小下,你的 .Net 字符串不再以正常方式进行垃圾收集。每次读取该文件时,最终都会将整个日志文件的另一个实例添加到内存中。
为了最好地建议如何解决这个问题,了解您的
LogText
变量使用哪种组件将会很有帮助。但在等待该信息之前,我至少可以提出一些建议:理想情况下,您只需保持文件打开(使用 FileShare.ReadWrite) 并在每次收到更改通知时从流中读取。但这并不总是可能的。
如果每次都必须重新打开文件,至少要逐行读取文本(使用 StreamReader),而不是使用 File.ReadAllLines() 一次拉出所有文本。这将帮助您将日志文件分成较小的部分,而不会最终出现在大型对象堆上。
不幸的是,我怀疑最终您将不得不构建一个大字符串来分配给纯文本框。如果是这种情况,我强烈建议您仅构建并显示日志的最后部分(少于 85000 字节),或者搜索大型对象堆安全文本框组件使用。
First of all, checking if the file exists is wrong. This is because the file system is volatile and because there is more than just existence at play (permissions, for example). The correct way to do this is to just open the file, and then handle the exception if it fails.
Now, on to your stated problem. What I suspect is happening is that the log is growing large enough to use the Large Object Heap (85000 bytes is all that's needed, iirc, and remember that .Net uses utf16 (2-byte) characters). A 43K ascii log file is all you'll need to start causing problems, because at that size your .Net string is no longer garbage collected in the normal way. Every time you read the file you end up adding another instance of the entire log file to memory.
To best recommend how to get around this, it will be helpful to know what kind of component you use for your
LogText
variable. But pending that information, I can at least suggest a few pointers:Ideally, you would just keep the file open (using FileShare.ReadWrite) and read from the stream every time you get a change notification. But that's not always possible.
If you have to re-open the file each time, at least read the text line by line (using a StreamReader) rather than pulling it all at once using File.ReadAllLines(). This will help you keep your log file broken up into smaller pieces that won't end up on the large object heap.
Unfortunately, I suspect that in the end you're stuck building one big string to assign to a plain textbox. If this is the case, I strongly recommend that you either only ever build and show the last part of the log (less than 85000 bytes worth) or that you search for a Large Object Heap-safe Textbox component to use.