c# winforms memory-leaks webbrowser-control

c# - Cómo arreglar la fuga de memoria en IE WebBrowser Control?



winforms memory-leaks (19)

Estoy tratando de incrustar un control WebBrowser en una aplicación de formato C #. Esto suena bastante fácil. Sin embargo, descubrí que el control WebBrowser consume mucha memoria cada vez que llamo al método Navigate. La memoria nunca se libera. El uso de la memoria crece y crece ...

Mucha gente en la red tiene exactamente el mismo problema pero todavía no he encontrado una respuesta satisfactoria. Estas son las mejores discusiones sobre este tema que he encontrado hasta ahora:

Fuga de memoria en IE WebBrowser Control

Una persona sugirió una actualización a IE8 para solucionar el problema.

Sin embargo, necesito una solución que funcione si el usuario tiene instalada la última versión de IE o no. No tengo control sobre el entorno de los usuarios.

¿Alguien sabe cómo liberar la memoria tomada por el control WebBrowser? ¿Hay soluciones temporales? ¿Hay alternativas al control WebBrowser?

Actualización: Acabo de hacer algunas pruebas más. En el trabajo, ejecuto Windows XP e IE6. La memoria no está creciendo allí. La memoria aumenta al llamar al método de navegación, pero se está liberando después de un tiempo. En casa estoy ejecutando Vista y actualicé a IE8. Aquí tampoco veo el problema. Parece que el problema es específico de IE7. Por lo tanto, la pregunta debe reformularse a "Cómo reparar la fuga de memoria en IE WebBrowser Control cuando IE7 está instalado". ¿Alguien puede confirmar que este problema es específico de IE7?


Acabo de crear una aplicación simple con un control de navegador web para tratar de duplicar los resultados. Lo que encontré fue que sí, cada vez que navegas hacia una página, la memoria que se usa aumenta significativamente. SIN EMBARGO, esto NO es una pérdida de memoria, porque si sigues navegando, verás que después de un momento, la memoria se reduce significativamente, lo que indica que el recolector de basura lo hizo. Para probarlo, obligué al recolector de basura a recolectar cada vez que llamé a Navigate, y la memoria general utilizada se mantuvo casi en la misma cantidad después de cada llamada de navegación.

Así que, aunque almacena memoria cada vez que "Navega", NO es una pérdida de memoria, y la memoria se liberará. Si está acumulando demasiado rápido, simplemente llame a GC.Collect ();


Busqué en Internet y no pude encontrar una respuesta a este problema. Lo arreglé usando el siguiente:

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

Sé que esta no es la solución más bonita o perfecta, pero funcionó muy bien para mí. - Layla


Creo que esta pregunta ha quedado sin respuesta durante mucho tiempo. Tantos hilos con la misma pregunta pero sin respuesta concluyente.

He encontrado una solución a este problema y quería compartir con ustedes todos los que aún enfrentan este problema.

Paso 1: cree un nuevo formulario, digamos form2 y agregue un control de navegador web en él. Paso 2: en el formulario 1 donde tienes el control de tu navegador web, solo quítalo. Paso 3: Ahora, vaya a Form2 y haga que el modificador de acceso para este control webbrowser sea público para que se pueda acceder en Form1 paso4: Cree un panel en form1 y cree el objeto de form2 y agréguelo al panel. Form2 frm = new Form2 (); frm.TopLevel = falso; frm.Show (); panel1.Controls.Add (frm); paso5: Llame al código siguiente a intervalos regulares frm.Controls.Remove (frm.webBrowser1); frm.Dispose ();

Eso es. Ahora cuando lo ejecutas, puedes ver que el control del webbrowser está cargado y se eliminará a intervalos regulares y ya no habrá más aplicaciones colgadas.

Puede agregar el código siguiente para hacerlo más eficiente.

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


Es casi a finales de 2017 y todavía este error molesto está presente en WebBrowser.

He intentado todas las soluciones aquí y ninguna de ellas funcionó para mí. La fuga de memoria aún persiste ... Lo más importante es que cuando llamo:

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

Realmente reduce la memoria mucho! pero cuando se llama a la próxima instrucción Navigate toda la memoria filtrada vuelve a estar en el alcance .... like (si la memoria está en 450mb ... estas instrucciones se reducen a aproximadamente 20 mb y justo después de llamar .Navegar (cadena) nuevamente salta a 460mb y la fuga de memoria continúa ...

Incluso probé Dispose (), navegando hasta about: blank antes de la página siguiente, estableciendo el objeto webbrowser como nulo y creando uno nuevo. Todos estos intentos caen en la fuga de memoria ... esto es realmente frustrante ... ¿Alguna otra solución?


Esto funcionó para mí, no estoy seguro de si el 100% borra la memoria, parece que todavía conserva las páginas almacenadas en la memoria caché, pero ya no asciende a 500MB de uso de ram, permanece en 60 MB.

mi programa va repetidamente al mismo sitio, 3 páginas diferentes, una sola vez por uso, no rastrea grandes cantidades de páginas ni nada.

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


Estoy usando Web Control en una aplicación, pero como mi aplicación solo navega a una página, no he notado el problema que mencionaste. Hay otro control web que en realidad es un contenedor y no sé si tiene el mismo problema o no. Puedes encontrarlo here .


Experimenté este problema con un simple control de webbrowser en un formulario. Navegó a una página web y se quedó allí. Según el administrador de tareas en 10 minutos, consumió 2 gb de memoria de 120 mb.

Una solución simple para mi proyecto fue ir a las propiedades de controles del webbrowser en Visual Studio y cambiar el ''Permitir navegación'' a falso. Ahora cuando ejecuto mi programa permanece en 120mb. ¡Espero que esto ayude a alguien!


Hay un control alternativo que usa Gecko (el motor que usa Firefox) en lugar de Trident y funciona muy bien con las interfaces MSHTML.

Tus páginas se procesarán en Gecko y tendrás control total sobre la configuración, los complementos, la seguridad y cualquier otra característica personalizable de un navegador.

La desventaja es que deberás enviar Gecko con tu aplicación, utilicé por última vez el equivalente a Firefox 2 y fue de alrededor de 8MB.

Lancé una aplicación hace bastante tiempo que comparaba el procesamiento de IE y Firefox uno con el otro, ambos actualizando mientras editabas el CSS. No me encontré con los problemas de memoria que tuvo con el control del navegador web, pero encontré que el control Gecko es muy fácil de usar. No tiene la misma clase de contenedor administrado que tiene el control .net WebBrowser, pero es bastante fácil solucionarlo.


Hay un Memory Leak conocido en el control WebBrowser. Consulte el siguiente artículo de Microsoft KB: KB893629 .


Intenté todas las soluciones anteriores, pero incluso una sola no funcionó (excepto el reinicio automático del programa, que no es una solución).

Luego renuncio así: "Ok, si quieres tanta memoria, entonces la tendrás y cambiarás mi aplicación a x64". Ya sabes lo que pasó?

Parece que no hay pérdida de memoria en la versión x64 de esta lib. Busqué en el administrador de tareas y mi aplicación nunca llegó a más de 300 MB, mientras que en x86 se comió 1.4 GB y luego se colgó.

Entonces ... tal vez esta información puede ayudarte.


La IDEA BÁSICA es,

"Matarme a mí mismo y renacer".

Windows resolverá todos los problemas de memoria.

pero si cierra su aplicación primero, no puede iniciar una nueva.

Entonces, INICIE UNO NUEVO, Y CIERRE EL MÁS VIEJO.

Primero enciende uno nuevo y apaga uno viejo.

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

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

Si hay un parámetro,

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

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

y, vaya a Form1.cs y haga otro Form1 ()

public Form1() { InitializeComponent(); } public Form1(string dance_name) { InitializeComponent(); ... }

o puedes usar el archivo temporal !!!


Me encontré con el mismo problema, como alternativa, en lugar de navegar a una nueva página, simplemente reescribí la misma página html usando el objeto system.oi.streamreader / writer y llamando a una actualización. Obviamente, eso no funcionará en una situación en la que el contenido para el navegador se esté alimentando en línea, pero me está haciendo el truco.

Además, actualmente estoy usando más de 8 controles de navegador, todos activos al mismo tiempo para servir informes a través de JavaScript dentro de mi aplicación .net. Cuando un usuario hace que un navegador esté activo, el html al que apuntan los otros navegadores se borra y los navegadores se actualizan. Con 8 navegadores funcionando con estos dos métodos, puedo mantener fácilmente mi aplicación bajo el uso de memoria de Firefox con solo 3 pestañas abiertas.


Me encontré con este problema al escribir una pequeña aplicación de "presentación de diapositivas" para diferentes páginas de intranet utilizadas por mi empresa. La solución más simple que encontré fue reiniciar la aplicación después de un período de tiempo fijo, una hora en mi caso. Esta solución funcionó bien para nosotros porque no había mucha interacción del usuario con el navegador.

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

Esto supone, por supuesto, que tiene un mecanismo de persistencia de datos en su lugar.


Parece que el método Navigate () mantiene todas las páginas visitadas en la memoria ya que puedes usar el método GoBack (), de hecho no hay "pérdida de memoria". Mi programa visita la misma Url repetidamente. El problema de "pérdida de memoria" puede eliminarse utilizando el método Refresh () instand del método Navigate (), seguido de un GC.Collect (). El código está en lo siguiente:

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


Pegue el siguiente código después de cargar la página

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


Según MSDN , el control System.Windows.Forms.WebBrowser es un contenedor gestionado para el control ActiveX WebBrowser y utiliza la versión del control que esté instalada en la computadora del usuario.

Puede encontrar el método Dispose (bool) en los metadatos de la clase WebBrowser (Presione F12 en Visual Stuio) para liberar recursos no administrados. (NOT Dispose ())

El código here

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

Pero si intenta llamar a WebBrowser.Dispose (bool), se muestra el error de compilación CS1540 .

La clase WebBrowser admite el método Dispose (bool), PERO no podemos usar eso.
Creo que la clase WebBrowser fue diseñada por el camino equivocado.

Tengo una idea para llamar WebBrowser.Dispose (true).
¡ES MUY SIMPLE! pero no es una buena manera.

Código de muestra aquí (3 botones y 1 necesidad de 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); } } }

Este código puede evitar la fuga de memoria.

En resumen, solo necesitas una clase.

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


Simplemente declare el control WebBrowser usando la palabra clave "using". Dejará de filtrar la memoria al llamar al método Navigate (). Lo probé y funcionó bien para mí.

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


Tenía los mismos problemas similares. Estaba enviando más de 5000 solicitudes de navegación a través del navegador web para eliminar páginas dinámicas. Después de aproximadamente 50 solicitudes, me quedaría sin memoria ya que el uso de la memoria de solicitud de navegación no se lanzó después de cada solicitud. Utilicé webBrowser.Dispose () después de la navegación, y resolvió el problema. No tiene que ver con IE7 más o menos. Estoy usando IE 11 y tengo el mismo problema. Fue porque no estaba eliminando los objetos de navegación. Espero que esto ayude.


mi aplicación también consumía constantemente memoria al navegar y ya no se liberaba. Encuentro la solución para mí aquí: http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/88c21427-e765-46e8-833d-6021ef79e0c8

para completar, publicar el fragmento notable:

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

- código para llamar cuando desee reducir la memoria

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

todos los honores a: 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 para publicar la solución.

y http://ict-engineer.blogspot.com/2010/10/net-webbrowser-control-memory-leak.html para SEO''ing it right, así que pude encontrarlo;)

saludos

editar: si esto te ayuda a resolver rápidamente un issu - bueno. pero deberías sobrepasar el diseño de tu aplicación, el patrón que usas, si es que hay alguna, refactorizar la cosa si construyes mucho más tiempo ...