2009-05-24 52 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並創建一個新的。這真的令人沮喪......任何其他解決方案?