如何修复IE WebBrowser控件中的内存泄漏?

我试图在C#Winform应用程序中嵌入一个WebBrowser控件。 这听起来很容易。 但是我发现每次调用Navigate方法时,WebBrowser控件都会占用大量内存。 永远不会释放内存。 内存使用量增长和增长……

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

IE WebBrowser控件中的内存泄漏

有人建议升级到IE8以解决问题。

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

有谁知道如何释放WebBrowser控件占用的内存? 有解决方法吗? 是否有WebBrowser控件的替代品?

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

我的应用程序在导航时也不断消耗内存,而不再发布。 我在这里为我提供解决方案: 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搜索它的权利,所以我可以找到它;)

问候

编辑:如果这有助于您快速解决问题 – 好。 但是你应该推翻你的应用程序设计,你使用的模式(如果有的话),如果你建立更长的时间,重构那个东西….

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

因此,虽然每次“导航”时它都会占用内存,但这不是内存泄漏,而是内存将被释放。 如果它的速度过快,只需调用GC.Collect();

BASIC IDEA是,

“杀死自己,重生。”

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(); ... } 

或者你可以使用临时文件!

有一个替代控件使用Gecko(Firefox使用的引擎)而不是Trident,并且与MSHTML接口配合得很好。

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

缺点是您需要使用您的应用程序运送Gecko,我上次使用相当于Firefox 2,它大约是8MB。

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

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

您可以在WebBrowser类的元数据中找到Dispose(bool)方法(在Visual Stuio中按F12)以释放非托管资源。(NOT Dispose())

这里的代码

 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个TextBox)

 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); } } } 

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

总之,您只需要一个class级。

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

WebBrowser控件中存在已知的内存泄漏。 请参阅以下Microsoft知识库文章 – KB893629 。

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

 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 

当然,这假定您具有数据持久性机制。

似乎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 

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

我遇到了同样的问题,作为替代方案,我只是使用system.oi.streamreader / writer对象重写同一个html页面并调用刷新,而不是导航到新页面。 显然,这不适用于浏览器的内容在线提供的情况,但它正在为我做这个伎俩。

此外,我目前正在使用8个以上同时处于活动状态的浏览器控件来通过我的.net应用程序中的javascript进行报告。 当用户激活一个浏览器时,将清除其他浏览器指向的html,并刷新浏览器。 有8个浏览器使用这两种方法运行,我可以轻松地将我的应用程序保持在Firefox的内存使用情况下,只打开3个选项卡。

我查看了整个互联网,我无法找到这个问题的答案。 我使用以下修复它:

 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 

我知道这不是最漂亮或最完美的解决方案,但它对我来说非常有效。 – 莱拉

页面加载后粘贴以下代码

 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); } 

我认为这个问题在很长一段时间内都没有得到答复。 这么multithreading有相同的问题,但没有确定的答案。

我已找到解决此问题的方法,并希望与您分享仍然面临此问题的所有人。

step1:创建一个新表单,例如form2,并在其上添加一个Web浏览器控件。 第2步:在您拥有webbrowser控件的form1中,只需将其删除即可。 步骤3:现在,转到Form2并将此webbrowser控件的访问修饰符设置为公共,以便可以在Form1中访问步骤4:在form1中创建一个面板并创建form2的对象并将其添加到面板中。 Form2 frm = new Form2(); frm.TopLevel = false; frm.Show(); panel1.Controls.Add(FRM); step5:定期调用下面的代码frm.Controls.Remove(frm.webBrowser1); frm.Dispose();

而已。 现在,当您运行它时,您可以看到加载了webbrowser控件,它将定期处理,并且不再挂起应用程序。

您可以添加以下代码,以提高效率。

  IntPtr pHandle = GetCurrentProcess(); SetProcessWorkingSetSize(pHandle, -1, -1); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); 

我有同样的问题。 我通过网络浏览器发送超过5000个导航请求以刮取动态页面。 在大约50个请求之后,由于在每个请求之后没有释放导航请求内存使用,我将耗尽内存。 我在导航后使用了webBrowser.Dispose(),它解决了这个问题。 它与IE7左右没有关系。 我正在使用IE 11并遇到同样的问题。 这是因为我没有处理导航对象。 希望这可以帮助。

这对我有用,不确定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(); } 

它几乎在2017年底,WebBrowser中仍然存在这个烦人的错误。

我在这里尝试了所有解决方案,但没有一个能为我工作。 memory leaks仍然存在……最奇怪的是,当我打电话时:

 GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); IntPtr pHandle = GetCurrentProcess(); SetProcessWorkingSetSize(pHandle, -1, -1); 

它实际上确实减少了内存! 但是当调用下一个Navigate指令时,所有泄漏的内存都会返回范围….就像(如果内存为450mb …这条指令减少到大约20 mb并且在调用之后再次.Navigate(字符串)再次跳转到460mb和内存泄漏继续…

我甚至尝试过Dispose(),在下一页之前导航到about:blank,将webbrowser对象设置为null并创建一个新对象。 所有这些尝试都落入了内存泄漏……这真是令人沮丧……还有其他任何解决方案吗?

只需使用“using”关键字声明WebBrowser控件。 它将在再次调用Navigate()方法时停止泄漏内存。 我只是测试了它,它对我来说很好。

 using (var webBrowser = new System.Windows.Forms.WebBrowser()) { webBrowser.Navigate(url); } 

我在表单上使用简单的webbrowser控件遇到了这个问题。 它导航到一个网页并留在那里。 根据任务经理的说法,他在10分钟内从120mb中获得了2GB的内存。

我的项目的一个简单解决方案是转到visual studio中的webbrowser控件属性并将’AllowNavigation’更改为false。 现在,当我运行我的程序时,它保持在120mb。 我希望这可以帮助别人!