我在 Windows 7、.NET 4.5、IE10 中。我有一个带有WebBrowser
控件的简单 Windows 窗体应用程序。每次页面导航时,我都想添加一个点击处理程序。我注意到当我这样做时内存正在泄漏。实际上,我正在分离事件处理程序,因此对内存泄漏的原因感到困惑。
我注意到我可以附加到HtmlDocument.Click
事件并且看不到与HtmlElement.Click
. 这在下面的程序中得到了演示,该程序使用计时器定期导航。
同样有趣的是,URL 似乎确实很重要......例如,我看到http://www.google.com的问题,但没有看到http://thin.npr.org(或 about:blank)的问题。这会让我相信这是一个javascript问题的核心,但是我目前正在使用的“修复”(见下文)与javascript没有任何关系......
那么是什么导致了问题以及如何解决这些问题,以便我的程序不会泄漏内存/句柄?
我目前的“修复”是拜访Marshal.ReleaseComObject
该HtmlElement.DomElement
物业。这是正确的做法吗?不应该像显然那样HtmlElement
清理自己吗?HtmlDocument
除了 之外,我还应该期待其他课程的问题HtmlElement
吗?
我很想在Marshal.ReleaseComObject
我看到 com 对象的任何地方慷慨地撒上(而不是假设包含类会正确地这样做)。这样做安全吗?
我的完整程序如下:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
public class Form1 : Form
{
Timer t;
WebBrowser webBrowser;
public Form1()
{
webBrowser = new WebBrowser
{
Dock = DockStyle.Fill,
Url = new Uri("http://www.google.com/")
};
webBrowser.DocumentCompleted += webBrowser_DocumentCompleted;
this.Controls.Add(webBrowser);
t = new Timer { Interval = 2000 };
t.Tick += t_Tick;
}
void t_Tick(object sender, EventArgs e)
{
t.Stop();
webBrowser.Navigate("http://www.google.com/");
}
void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (webBrowser.ReadyState == WebBrowserReadyState.Complete)
{
var doc = webBrowser.Document;
if (doc == null) //no page loaded
return;
//the following two lines do not cause a leak
doc.Click += click_handler;
doc.Click -= click_handler;
var body = doc.Body;
if (body != null)
{
//the following 2 lines cause memory and handle leaks
body.Click += click_handler;
body.Click -= click_handler;
//uncommenting the following line appears to fix the leak
//Marshal.ReleaseComObject(body.DomElement);
body = null;
}
GC.Collect(2, GCCollectionMode.Forced);
t.Start();
}
}
void click_handler(object sender, HtmlElementEventArgs e)
{
}
}
}
对于任何好奇的人,我在我的程序中使用对象进行了测试,WeakReference
并body
验证它正在被垃圾收集。
在运行“固定”版本一段时间(可能是一个小时)后,它稳定在约 40 兆的私有字节和 640 个句柄。“泄漏”版本达到数百兆和数千个句柄,并且似乎没有放慢速度。在生产中,当事情停止运行(图像无法加载)时,用户有一个使用超过 900 兆的进程。关闭应用程序需要几秒钟才能将所有内容都删除。