2009-05-24 94 views
27

我想在C#Winform应用程序中嵌入一个WebBrowser控件。这听起来很简单。但是,我发现每次调用Navigate方法时,WebBrowser控件都会消耗大量内存。内存永远不会释放。内存使用量增长和增长...如何修复IE WebBrowser控件中的内存泄漏?

许多人在网上有完全相同的问题,但我还没有找到一个令人满意的答案呢。这是关于这个问题的最佳讨论,我发现迄今:

Memory Leak in IE WebBrowser Control

一个人建议升级到IE8来解决这个问题。

但是,我需要一个解决方案,无论用户是否安装了最新的IE版本都可以使用。我无法控制用户环境。

有人知道如何释放WebBrowser控件所占用的内存吗?有解决方法吗? WebBrowser控件是否有其他选择?

更新: 我只是做了更多的测试。在工作中,我运行Windows XP和IE6。记忆不在那里增长。调用导航方法时内存会增加,但在一段时间后会释放内存。在家里我正在运行Vista并升级到IE8。在这里,我也没有看到问题了。看起来这个问题是针对IE7的。所以这个问题应该改为“IE7安装时如何修复IE浏览器控制中的内存泄漏”。任何人都可以确认这个问题是特定于IE7?

+0

还有,如果你的事件处理程序添加到任何的导航元素的内存泄漏。为了解决这个问题,你必须保留所有元素的字典(也包括顶级文档),然后在OnDocumentCompleted()函数中,逐个删除事件处理程序,同时在调用marshall.ReleaseComObject(o.DomDocument)循环,然后通过Marshal.ReleaseComObject(document.DomDocument)最终释放顶层文档。 – Brain2000 2017-04-10 17:07:40

回答

0

我在应用程序中使用Web控件,但由于我的应用程序仅导航到一个页面,所以我没有注意到您提到的问题。 还有另一个网页控件实际上是一个包装,我不知道它是否有相同的问题。 你可以找到它here

3

有一个alternative control使用壁虎(引擎Firefox使用),而不是三叉戟,并与MSHTML接口工作得很好。

您的页面将在Gecko中呈现,您将完全控制浏览器的设置,插件,安全性和任何其他可自定义的功能。

缺点是您需要将Gecko与您的应用程序一起发布,我上次使用的是Firefox 2的等效版本,大约为8MB。

不久前,我发布了一个应用程序,它将IE和Firefox渲染器相互比较,并在编辑CSS时进行更新。我没有遇到你使用网络浏览器控制的内存问题,但我发现Gecko控件很容易处理。它没有与.net WebBrowser控件具有的相同的托管包装类,但很容易解决这个问题。

+0

watin类可以与Gecko一起使用吗? – atwellpub 2012-03-23 21:05:30

8

我刚刚创建了一个带有Web浏览器控件的简单应用程序,以尝试重复您的结果。我发现是的,每当你浏览一个页面,正在使用的内存就会显着增加。但是,这不是内存泄漏,因为如果你继续导航,你会发现在一段时间后,内存会显着下降,表明垃圾收集器做的是事情。为了证明这一点,我在每次打电话给Navigate之后都强制收集垃圾收集器,并且在每次导航调用之后,所用的整个内存保持几乎相同的数量。

所以虽然它每次“导航”这不是一个内存泄漏时间架起来的内存,你的内存将被释放。如果速度过快,只需调用GC.Collect();

+1

我只是做了更多的测试。在工作中,我运行Windows XP和IE6。记忆不在那里增长。就像BFree所描述的那样:调用导航方法时内存增加,但稍后会释放内存。在家里我正在运行Vista并升级到IE8。在这里,我也没有看到问题了。看起来这个问题是针对IE7的。 – 2009-05-26 20:34:42

+0

是的,最好用IE6来看。与更新的浏览器它变得更好。 是减少对GC-收集 - 但消耗的一般内存increading,而不是减少了 - 任务管理器显示该程序所消耗很少的内存,但系统内存被填满,直到应用程序被关闭......然后等待一点点,内存再次被释放...我会朝着zuvector说的方向前进,我会转储到pagefile .... – womd 2014-02-14 09:51:02

0

我遇到了同样的问题,作为替代方法,而不是导航到新页面,我只是使用system.oi.streamreader/writer对象重写同一个html页面并调用刷新。很明显,这种方式在浏览器内容被在线提供的情况下不起作用,但它对我来说是个诀窍。

而且,我目前使用的浏览器8+控制,同时服务于通过我的.net应用程序内的javascript报告所有活动。当用户使一个浏览器处于活动状态时,其他浏览器所指向的html将被清除并刷新浏览器。有8个浏览器运行这两种方法,我只需打开3个选项卡,就可以轻松地将我的应用程序保存在Firefox的内存使用情况下。

1

WebBrowser控件中有一个已知的内存泄漏。请参阅以下Microsoft知识库文章 - KB893629

11

导航时,我的应用程序也被不断消耗内存,而不是释放了。 我发现那位解决方案我在这里: http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/88c21427-e765-46e8-833d-6021ef79e0c8

的完整性生病后的显着的摘录:

-- in class definition 

    [DllImport("KERNEL32.DLL", EntryPoint = "SetProcessWorkingSetSize", SetLastError = true, CallingConvention = CallingConvention.StdCall)] 
    internal static extern bool SetProcessWorkingSetSize(IntPtr pProcess, int dwMinimumWorkingSetSize, int dwMaximumWorkingSetSize); 

    [DllImport("KERNEL32.DLL", EntryPoint = "GetCurrentProcess", SetLastError = true, CallingConvention = CallingConvention.StdCall)] 
    internal static extern IntPtr GetCurrentProcess(); 

- 代码,当你想减少内存

 IntPtr pHandle = GetCurrentProcess(); 
     SetProcessWorkingSetSize(pHandle, -1, -1); 

所有来电荣誉为: http://social.msdn.microsoft.com/profile/mike_t2e/?type=forum&referrer=http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/88c21427-e765-46e8-833d-6021ef79e0c8 发布解决方案。

http://ict-engineer.blogspot.com/2010/10/net-webbrowser-control-memory-leak.html 为SEO'ing是正确的,所以我能找到它;)

问候

编辑:如果这可以帮助您快速解决的ISSU - 好。但你应该overthing您的应用程序设计,您可以使用,如果任何模式,如果你建立refactore的东西到长得多....

+4

我试过这个,它所做的只是让它看起来像它使用更少的内存任务管理器,但是应用程序在一段时间后变得没有反应,就像我没有调用这个函数一样。这是线程:http://stackoverflow.com/questions/6147923/webbrowser-memory-problem – Juan 2011-06-27 08:19:54

+17

这不是一个解决问题,它只是隐藏它。 Windows继续使用过量的内存,只是立即进入页面文件内存(您可以在页面错误列下的任务管理器中看到此内容),而不是物理内存。这意味着每次尝试将此解决方案的使用降至最低时,Windows都会将泄漏内存写入页面文件中的磁盘。最后,这意味着您的应用程序将比以前更慢,因为磁盘驱动器会周期性地出现抖动,并且您仍然会耗尽页面文件和RAM。这不会解决任何问题。 – Azuvector 2011-11-24 00:08:00

+0

是,呼唤它memoryleak是有点粗鲁 - 它只是“历史”是变大了......阿龙与其他功能,使应用程序,即如此丰富......重现你必须visist约100页的内存的东西,他们也可以在同一个网站上。而且我也确认,“使用新的浏览器和足够的内存时,内存事情会变得更好......但在那些我用这个2GB内存打得不错的日子里) - 当我应用这个”memory-reset “在我的应用程序,通过它跑了几个星期,梅比只是运气好 - 梅比只是创造一个新的浏览器,ojbect时不时也会做.. – womd 2012-05-16 14:28:30

0

我看着所有在互联网上,我无法找到答案这个问题。我使用下面的方法修复它:

Protected Sub disposeBrowers() 
    If debug Then debugTrace() 
    If Me.InvokeRequired Then 
     Me.Invoke(New simple(AddressOf disposeBrowers)) 
    Else 
     Dim webCliffNavigate As String = webCliff.Url.AbsoluteUri 

     'Dim webdollarNavigate As String = webDollar.Url.AbsoluteUri 
     Me.splContainerMain.SuspendLayout() 
     Me.splCliffDwellers.Panel2.Controls.Remove(webCliff) 
     Me.splDollars.Panel2.Controls.Remove(webDollar) 
     RemoveHandler webCliff.DocumentCompleted, AddressOf webCliff_DocumentCompleted 
     RemoveHandler webDollar.DocumentCompleted, AddressOf webDollar_DocumentCompleted 
     RemoveHandler webCliff.GotFocus, AddressOf setDisposeEvent 
     RemoveHandler webCliff.LostFocus, AddressOf setDisposeEvent 
     RemoveHandler webDollar.GotFocus, AddressOf setDisposeEvent 
     RemoveHandler webDollar.LostFocus, AddressOf setDisposeEvent 
     webCliff.Stop() 
     webDollar.Stop() 

     Dim tmpWeb As SHDocVw.WebBrowser = webCliff.ActiveXInstance 
     System.Runtime.InteropServices.Marshal.ReleaseComObject(tmpWeb) 
     webCliff.Dispose() 

     tmpWeb = webDollar.ActiveXInstance 
     System.Runtime.InteropServices.Marshal.ReleaseComObject(tmpWeb) 
     webDollar.Dispose() 

     webCliff = Nothing 
     webDollar = Nothing 
     GC.AddMemoryPressure(50000) 
     GC.Collect() 
     GC.WaitForPendingFinalizers() 
     GC.Collect() 
     GC.WaitForFullGCComplete() 
     GC.Collect() 
     GC.RemoveMemoryPressure(50000) 
     webCliff = New WebBrowser() 
     webDollar = New WebBrowser() 
     webCliff.CausesValidation = False 
     webCliff.Dock = DockStyle.Fill 
     webDollar.CausesValidation = webCliff.CausesValidation 
     webDollar.Dock = webCliff.Dock 
     webDollar.ScriptErrorsSuppressed = True 
     webDollar.Visible = True 
     webCliff.Visible = True 
     Me.splCliffDwellers.Panel2.Controls.Add(webCliff) 
     Me.splDollars.Panel2.Controls.Add(webDollar) 
     Me.splContainerMain.ResumeLayout() 

     'vb.net for some reason automatically recreates these and the below is not needed 
     'AddHandler webCliff.DocumentCompleted, AddressOf webCliff_DocumentCompleted 
     'AddHandler webDollar.DocumentCompleted, AddressOf webDollar_DocumentCompleted 
     'AddHandler webCliff.GotFocus, AddressOf setDisposeEvent 
     'AddHandler webCliff.LostFocus, AddressOf setDisposeEvent 
     'AddHandler webDollar.GotFocus, AddressOf setDisposeEvent 
     'AddHandler webDollar.LostFocus, AddressOf setDisposeEvent 

     webCliff.Navigate(webCliffNavigate) 
     'webDollar.Navigate(webdollarNavigate) 
     disposeOfBrowsers = Now.AddMinutes(20) 
    End If 
End Sub 

我知道这不是最漂亮或完美的解决方案,但它对我来说工作得非常好。 - 莱拉

0

页面加载

System.Diagnostics.Process loProcess = System.Diagnostics.Process.GetCurrentProcess(); 
try 
{ 
    loProcess.MaxWorkingSet = (IntPtr)((int)loProcess.MaxWorkingSet - 1); 
    loProcess.MinWorkingSet = (IntPtr)((int)loProcess.MinWorkingSet - 1); 
} 
catch (System.Exception) 
{ 
    loProcess.MaxWorkingSet = (IntPtr)((int)1413120); 
    loProcess.MinWorkingSet = (IntPtr)((int)204800); 
} 
0

后,下面的代码粘贴我认为这个问题已经很长时间没有回音了。所以很多有相同问题的线程,但没有确凿的答案。

我已经找到了解决有关此问题,并希望与大家分享所有谁仍然面临这个问题。

第一步:创建一个新的形式说窗口2并在其上添加一个网络浏览器控制。第二步:在你有你的webbrowser控件的form1中,只需删除它。第3步:现在,转到Form2并使此浏览器控件的访问修饰符处于公用状态,以便可以在Form1中访问它。step4:在form1中创建一个面板并创建form2的对象并将其添加到面板中。 Form2 frm = new Form2(); frm.TopLevel = false; frm.Show(); panel1.Controls.Add(FRM);第5步:定期调用下面的代码frm.Controls.Remove(frm.webBrowser1); frm.Dispose();

就是这样。现在,当您运行它时,您会看到加载了Web浏览器控件,并且会定期处理它,并且不会再挂起应用程序。

您可以添加下面的代码,使其更高效。

IntPtr pHandle = GetCurrentProcess(); 
    SetProcessWorkingSetSize(pHandle, -1, -1); 


    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 
    GC.Collect(); 
1

我就遇到了这个问题,同时写作由我公司采用不同的内部网页一个小型的“幻灯片”的应用程序。我发现最简单的解决方案是在一段固定时间后重新启动应用程序,在我的情况下是一个小时。这个解决方案对我们来说效果很好,因为没有很多用户与浏览器进行交互。

Public Class MyApplication 

    Private _AppTimer As Timers.Timer 

    Public Sub New() 
     _AppTimer = New Timers.Timer() 
     _AppTimer.Interval = 1 * 60 * 60 * 1000 '1 Hour * 60 Min * 60 Sec * 1000 Milli 

     AddHandler _AppTimer.Elapsed, AddressOf AppTimer_Elapsed 

     _AppTimer.Start() 
    End Sub 

    Private Sub AppTimer_Elapsed(s As Object, e As Timers.ElapsedEventArgs) 
     Application.Restart() 
    End Sub 

End Class 

这当然假设你有一个数据持久性机制。

1

看起来Navigate()方法将所有访问过的页面保存在内存中,因为您可以使用GoBack()方法,事实上并没有“内存泄漏”。我的程序反复访问同一个Url。通过使用Navigate()方法的Refresh()方法,然后使用GC.Collect(),可以消除“内存泄漏”问题。该代码是在以下几点:

 try 
     { 
      if (webBrowser.Url.Equals("about:blank")) //first visit 
      { 
       webBrowser.Navigate(new Uri("http://url")); 
      } 
      else 
      { 
       webBrowser.Refresh(WebBrowserRefreshOption.Completely); 
      } 
     } 
     catch (System.UriFormatException) 
     { 
      return; 
     } 
     System.GC.Collect(); // may be omitted, Windows can do this automatically 
5

的基本思路是,

“杀自己,重获新生。”

Windows将解决所有内存问题。

但是,如果您先关闭应用程序,则无法启动新的应用程序。

因此,开始一个新的,并关闭下一个。

首先打开一个新的,关闭一个旧的。


public void SOLVE_ALL_MY_MEMORY_PROBLEM() 
{ 
    System.Diagnostics.Process.Start("MyProgram.exe"); 
    Application.Exit(); 
} 

https://www.youtube.com/watch?v=aTBlKRzNf74

如果有一个参数,

public void SOLVE_ALL_MY_MEMORY_PROBLEM() 
{ 
    System.Diagnostics.Process.Start("MyProgram.exe", "PARA_para_dance"); 
    Application.Exit(); 
} 

转到Program.cs的

static void Main(string[] args) 
    { 
     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 
     if(args.Count() > 0) 
      Application.Run(new Form1(args[0])); 
     else 
      Application.Run(new Form1()); 
    } 

,并转到Form1.cs和再拍Form1中()

public Form1() 
    { 
     InitializeComponent(); 
    } 

    public Form1(string dance_name) 
    { 
     InitializeComponent(); 

     ... 
    } 

,或者您可以使用临时文件!

0

我也有类似的问题。我通过网络浏览器发送了超过5000条导航请求来抓取动态页面。经过大约50次请求后,由于每次请求后都没有释放导航请求内存使用情况,因此内存不足。我在导航后使用了webBrowser.Dispose(),并解决了问题。它不必与IE7左右。我使用IE 11并得到同样的问题。这是因为我没有配置导航对象。 希望这有助于。

0

这对我有用,不知道如果100%清除内存,它似乎仍然保持缓存页面,但不再攀升到500MB RAM的使用,它保持在60MB。

我的程序反复进入同一个网站,3个不同的网页,每次只能使用一次,不会抓取大量的网页或任何内容。

string eventBuffer; 

void GetContracts_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) 
     { 
      var web = sender as WebBrowser; 
      if (web.Url == e.Url) 
      { 
       TaskMaster.Get_Contracts(ref web); 
       if(Memory.Contracts.Count==0) 
       { 
        eventBuffer="UpdateContractFailed"; 
        web.Disposed += new EventHandler(web_Disposed); 
        web.Dispose(); 
        return; 
       } 
       eventBuffer="UpdateContractList"; 
       web.Disposed += new EventHandler(web_Disposed); 
       web.Dispose(); 
      } 
     } 

private void web_Disposed(object sender, EventArgs e) 
     { 
      FireEvent(eventBuffer); 
      GC.Collect(); 
      thread.Abort(); 
     } 
2

MSDN,该System.Windows.Forms.WebBrowser控件是为ActiveX WebBrowser控件托管包装,并会使用任何版本的控件安装在用户的计算机上。

你可以找到的Dispose(布尔)在web浏览器类(在Visual Stuio按F12键)来释放非托管资源的元数据方法。(不要将其())

代码here

protected override void Dispose(bool disposing) { 
    if (disposing) { 
     if (htmlShimManager != null) 
     { 
      htmlShimManager.Dispose(); 
     } 
     DetachSink(); 
     ActiveXSite.Dispose(); 
    } 
    base.Dispose(disposing); 
} 

但是,如果您尝试调用WebBrowser.Dispose(bool),则会显示编译器错误CS1540

WebBrowser类支持Dispose(bool)方法,但我们不能使用它。
我认为WebBrowser类的设计是错误的。

我有一个想法来调用WebBrowser.Dispose(true)。
这很简单!但它不是一个好方法。在这里

示例代码(3个按钮和文本框1个需要)

using System; 
using System.Drawing; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 

namespace Test_20170308_01 
{ 
    public partial class Form1 : Form 
    { 
     [DllImportAttribute("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)] 
     private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize); 
     public static void FlushMemory() 
     { 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      if (Environment.OSVersion.Platform == PlatformID.Win32NT) 
      { 
       SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1); 
      } 
     } 

     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void addWeb() 
     { 
      WebBrowserD webBrowser1 = new WebBrowserD(); 
      webBrowser1.Size = new Size(1070, 585); 
      this.Controls.Add(webBrowser1); 
      webBrowser1.Navigate("about:blank"); 
     } 

     private void RemoveWeb() 
     { 
      foreach (Control ctrl in this.Controls) 
      { 
       if (ctrl is WebBrowserD) 
       { 
        WebBrowserD web = (WebBrowserD)ctrl; 
        this.Controls.Remove(ctrl); 
        web.Navigate("about:blank"); 
        web.Dispose(true); 
        FlushMemory(); 
       } 
      } 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      addWeb(); 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      RemoveWeb(); 
     } 

     private void button3_Click(object sender, EventArgs e) 
     { 
      foreach (Control ctrl in this.Controls) 
      { 
       if (ctrl is WebBrowserD) 
       { 
        WebBrowserD axweb = (WebBrowserD)ctrl; 
        axweb.Navigate(textBox1.Text); 
        FlushMemory(); 
       } 
      } 
     } 
    } 

    public class WebBrowserD : WebBrowser 
    { 
     internal void Dispose(bool disposing) 
     { 
      // call WebBrower.Dispose(bool) 
      base.Dispose(disposing); 
     } 
    } 
} 

此代码,可以防止内存泄漏。

综上所述, 你只需要一类。

public class WebBrowserD : WebBrowser 
    { 
     internal void Dispose(bool disposing) 
     { 
      base.Dispose(disposing); 
     } 
    } 
0

它快到2017年底了,而且这个烦人的bug仍然存在于WebBrowser中。

我已经尝试过所有的解决方案,没有人为我工作。内存泄漏仍然存在...的shtrangest的是,当我打电话:

GC.Collect(); 
GC.WaitForPendingFinalizers(); 
GC.Collect(); 

IntPtr pHandle = GetCurrentProcess(); 
SetProcessWorkingSetSize(pHandle, -1, -1); 

它实际上减少了内存了很多!但是当下一个Navigate指令被调用时,所有泄漏的内存都会返回到范围内......比如(如果内存在450mb ..这个指令减少到大约20mb,并且在调用后立即调用。再次导航(字符串)它跳转到了460mb和内存泄漏继续...

我甚至试过Dispose(),在下一页之前导航到about:blank,将webbrowser对象设置为null并创建一个新的。这真的令人沮丧......任何其他解决方案?