网络浏览器内存问题
我有一个 .NET 应用程序,需要使用 WebBrowser
自动浏览一堆页面。但是,例如,如果我访问 Google 并打开 Google Instant,然后搜索任何内容并多次手动导航到下一个按钮,我的应用程序使用的内存将开始增加。
问题可能是 Google 即搜即得以某种方式保留了以前页面的数据,但即使在我导航到其他位置(例如“about:blank”)后,使用的内存也不会减少。 IE 9 也会出现此问题。我开始记下第 60 页使用的内存,这就是我得到的结果(使用 IE 9):
Page 60: 180 MB
Page 70: 214 MB
Page 80: 245 MB
Page 90: 280 MB
因此,如您所见,每 10 页内存几乎线性增加 30 - 35 MB。如果在我离开谷歌后内存被释放,这就不成问题了。但事实并非如此。
我也尝试过这个但没有做任何事情。
编辑:我做了一个项目只是为了测试这个。这是我的 Form1
代码:
namespace WebBrowserMemoryTest
{
public partial class Form1 : Form
{
private int _Pages;
public Form1()
{
InitializeComponent();
webBrowser1.Navigate("http://www.google.com");
}
private void startButton_Click(object sender, EventArgs e)
{
_Pages = 0;
timer1.Start();
}
private void stopButton_Click(object sender, EventArgs e)
{
timer1.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
HtmlElement next = webBrowser1.Document.GetElementById("pnnext");
if (_Pages <= 90)
{
if (null != next)
{
string href = next.GetAttribute("href");
webBrowser1.Navigate(href);
_Pages++;
}
else
{
timer1.Stop();
MessageBox.Show("Next button not found");
}
}
else
{
timer1.Stop();
MessageBox.Show("Done");
}
}
private void goButton_Click(object sender, EventArgs e)
{
webBrowser1.Navigate(textBox1.Text);
}
private void freeMemButton_Click(object sender, EventArgs e)
{
MemoryManagement.FlushMemory();
}
}
public class MemoryManagement
{
[DllImport("kernel32.dll")]
public static extern bool SetProcessWorkingSetSize(IntPtr proc, int min, int max);
public static void FlushMemory()
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
}
}
}
}
我所做的就是在 Google Instant 开启的情况下在 Google 中搜索任何内容,然后按 startButton
(这会调用 <代码>startButton_Click)。大约 80 页后,我按下 stopButton
,然后导航到“about:blank”,然后返回 Google 并搜索其他内容,然后再次按下 startButton
。
我首先在我的 PC 上进行了测试,它有 6 GB 内存。当我达到 1.5 GB 时,应用程序停止响应,但我没有收到任何 OutOfMemory
异常。然后我在装有 Windows 7 和 1 GB RAM 的虚拟机上进行了测试。当它达到大约 300 MB 时,我的应用程序中的 Web 浏览器变得没有响应。
如果我按下 freeMem
按钮,该按钮会调用 freeMemButton_Click
,内存就会重新下降(但请参阅我的 Edit2)。这样就“解决”了我的问题。但现在我的问题是为什么我需要调用SetProcessWorkingSetSize
? Windows不是应该自动释放内存吗?另外,我不确定调用该函数是否会产生任何副作用。
我很确定这是一个错误。我应该继续报告吗?
编辑2:我测试了Stefan的解决方案(调用SetProcessWorkingSetSize(GetCurrentProcess(), -1, -1)
),但没有修复它。任务管理器的内存下降了,但这只是明显的。在不调用该函数时,经过多次浏览器导航后,应用程序变得无响应。
I have a .NET application that needs uses a WebBrowser
to automatically navigate through a bunch of pages. But if I go to, for instance, Google and set Google Instant on, and then search anything and navigate manually through the next button several times, the memory used by my application will start increasing.
The problem might be that Google Instant is somehow keeping data from previous pages, but even after I navigate somewhere else, such as "about:blank", the memory used won't decrease. This problem also occurs with IE 9. I started writing down my memory used at page 60 and this is what I got (with IE 9):
Page 60: 180 MB
Page 70: 214 MB
Page 80: 245 MB
Page 90: 280 MB
So as you can see, the memory increases almost lineally by 30 - 35 MB every 10 pages. This wouldn't be a problem if the memory would be released after I navigate away from Google. But is not.
I also tried this and didn't do anything.
Edit: I made a project just to test this. Here is my Form1
code:
namespace WebBrowserMemoryTest
{
public partial class Form1 : Form
{
private int _Pages;
public Form1()
{
InitializeComponent();
webBrowser1.Navigate("http://www.google.com");
}
private void startButton_Click(object sender, EventArgs e)
{
_Pages = 0;
timer1.Start();
}
private void stopButton_Click(object sender, EventArgs e)
{
timer1.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
HtmlElement next = webBrowser1.Document.GetElementById("pnnext");
if (_Pages <= 90)
{
if (null != next)
{
string href = next.GetAttribute("href");
webBrowser1.Navigate(href);
_Pages++;
}
else
{
timer1.Stop();
MessageBox.Show("Next button not found");
}
}
else
{
timer1.Stop();
MessageBox.Show("Done");
}
}
private void goButton_Click(object sender, EventArgs e)
{
webBrowser1.Navigate(textBox1.Text);
}
private void freeMemButton_Click(object sender, EventArgs e)
{
MemoryManagement.FlushMemory();
}
}
public class MemoryManagement
{
[DllImport("kernel32.dll")]
public static extern bool SetProcessWorkingSetSize(IntPtr proc, int min, int max);
public static void FlushMemory()
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
}
}
}
}
What I do is search anything in Google with Google Instant turned on, and then press the startButton
(which invokes startButton_Click
). After about 80 pages I press the stopButton
, then navigate to "about:blank", then go back to Google and search anything else and press the startButton
again.
I first tested this on my PC, which has 6 GB ram. When I reached 1.5 GB the application stopped responding but I didn't get any OutOfMemory
exception. Then I tested it on a virtual machine with Windows 7 and 1 GB ram. When it reached about 300 MB the web browser in my application became unresponsive.
If I press the freeMem
button, which calls freeMemButton_Click
, the memory goes back down (but see my Edit2). So that "solves" my problem. But now my question is why do I need to call SetProcessWorkingSetSize
? Isn't it Windows supposed to free the memory automatically? Also, I'm not sure if calling that function will have any side effect.
I'm pretty sure this is a bug. Should I go ahead and report it?
Edit2: I tested Stefan's solution (calling SetProcessWorkingSetSize(GetCurrentProcess(), -1, -1)
) and didn't fix it. The memory went down on the task manager, but this is only apparent. The application became unresponsive after as many browser navigations it took to become unresponsive when not calling that function.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为 HtmlElement pnext 尚未发布,因为它是 ComObject,并且浏览器控件中可能存在错误。
尝试 pnext.Release 或尝试 Marshal.Release 并获取要释放的 ComObject 实例。
I think HtmlElement pnext is not released, since it's ComObject, and there may be a bug in Browser Control.
Try pnext.Release or try Marshal.Release and get the instance of ComObject to release.
在使用 Webbrowser 时,我发现等待“documentcompleted”事件并在不导航下一页之前检查“状态”是否“已完成”非常重要。否则取消导航并在导航之前再次等待文档完成状态(中止)....
可能是使用计时器而不检查 Web 浏览器的状态可能是一个问题
Webbrowser-Control 的下一件事是,你不能在多线程/Backgroundworker中使用它,因为它需要是STA....(经常会导致应用程序无响应的问题)
使用“ful-contol”(解析网站)的解决方案.NET 对我来说,是使用 WebRequest / Webresponse ,如果你希望 DOM “自动”执行,你可以将其结果再次投射到 Webbroswer.Document ,使用多线程,轻松设置超时/代理,我觉得更舒服比使用 Webbrowser 控件。
尽管如此,我使用此处的提示成功地在另一个项目中实现了 Webbrowser-Control 解析页面( 如何修复 IE WebBrowser 控件中的内存泄漏?)
另一个“有趣”的瘦身我发现 Winforms 编程,当窗口最小化并再次打开时,似乎会触发 GC.Collect - >这会减少内存使用吗? (也许Stefan的帖子也提到了这个问题)
when working with Webbrowser, i found that it is important to wait for the "documentcompleted" event and check if the "state" is "completed" bevore navigating no next page. Else cancel navigation and wait again for document-completed state (aborted ) bevore navigating....
could be that the use of timers and not checking the Webbrowser's state could be a problem
the next thing with the Webbrowser-Control is, that you cannot use it in multithread / Backgroundworker since it requires to be STA....( often makes problems with unresponsive application )
the solution for "ful-contol" (parsing websites ) using .NET for me, was to use WebRequest / Webresponse , you can cast the result of that to Webbroswer.Document again if you want the DOM to be "auto" executed , use it multithreaded , easyly set timeouts / proxy wich i found more comfortable than using Webbrowser-control.
Nevertheless i sucessfully implemented a Webbrowser-Control parsing pages in another project using hints from here ( How to Fix the Memory Leak in IE WebBrowser Control?)
another "funny" thin with Winforms-programming i found out is, that it seems that a GC.Collect is triggered when the window is minimized an opened again - > does this decrease the mem-usage ? ( maybee Stefan's post also references to this issue )
如果没有任何原因,Windows 不会真正返回已释放的内存。唯一的原因是如果另一个应用程序需要该内存并且不再有其他可用内存。这就是为什么看起来内存使用量增加了。
尝试调用
SetProcessWorkingSetSize(GetCurrentProcess(), - 1, -1);
有时 - 这将迫使操作系统将所有释放的内存返回给操作系统。
Windows doesn't really return freed memory if there's nor reason for it. And the only reason would be if another app requires that memory and there's no other memory available anymore. That's why it looks as if the memory use increases.
Try calling
SetProcessWorkingSetSize(GetCurrentProcess(), -1, -1);
sometimes - this will force the OS to return all freed memory back to the OS.