3

我在 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.ReleaseComObjectHtmlElement.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)
        {
        }
    }
}

对于任何好奇的人,我在我的程序中使用对象进行了测试,WeakReferencebody验证它正在被垃圾收集。

在运行“固定”版本一段时间(可能是一个小时)后,它稳定在约 40 兆的私有字节和 640 个句柄。“泄漏”版本达到数百兆和数千个句柄,并且似乎没有放慢速度。在生产中,当事情停止运行(图像无法加载)时,用户有一个使用超过 900 兆的进程。关闭应用程序需要几秒钟才能将所有内容都删除。

4

0 回答 0