首先介紹一下記憶體洩漏(Memory Leak)的概念,記憶體洩露是指程式中已動态配置設定的堆記憶體由于某種原因未釋放或者無法釋放,造成系統記憶體的浪費,導緻程式運作速度減慢甚至系統崩潰等嚴重後果。
最近在使用WPF WebBrowser時,就遇到了Memory Leak的問題。
在主窗體上通過一個按鈕點選事件加載包含有WebBrowser控件的窗體,使用這個WebBrowser來浏覽網頁,然後調用WebBrowser的Dispose()方法,然後調用GC.Collect(),最後關閉目前包含有WebBrowser控件的窗體。
通過下面的代碼和步驟來還原這個問題。
MainWindow.xaml
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Button Content="Launch Browser Window" x:Name="btnLaunchNewWindow" Margin="5,0,5,0" Click="btnLaunchNewWindow_Click" />
<Button Content="Force Garbage Collection" x:Name="btnForceGarbageCollection" Click="btnForceGarbageCollection_Click" />
<Button Content="Quit" x:Name="btnQuit" Click="btnQuit_Click" />
</StackPanel>
private void btnLaunchNewWindow_Click(object sender, RoutedEventArgs e)
{
new BrowserWindow().Show();
}
private void btnForceGarbageCollection_Click(object sender, RoutedEventArgs e)
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
}
private void btnQuit_Click(object sender, RoutedEventArgs e)
{
Environment.Exit(0);
}
BrowserWindow.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Label Content="URL:" />
<TextBox x:Name="txtURL" Width="400" />
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,10">
<Button Content="1. Go/Navigate" x:Name="btnGo" Click="btnGo_Click" />
<Button Content="2. Dispose" x:Name="btnClose" Click="btnClose_Click" Margin="10,0" />
<Button Content="3. Force Garbage Collection" x:Name="btnForceGarbageCollection" Click="btnForceGarbageCollection_Click" />
<Button Content="4. Close Window" x:Name="closeWindow" Click="closeWindow_Click" Margin="10,0" />
</StackPanel>
<WebBrowser Grid.Row="2" x:Name="webBrowser" />
</Grid>
private void btnGo_Click(object sender, RoutedEventArgs e)
{
try
{
webBrowser.Navigate(new Uri(txtURL.Text));
}
catch (Exception ex)
{
MessageBox.Show("Exception: " + ex.Message);
}
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
webBrowser.Dispose();
}
private void btnForceGarbageCollection_Click(object sender, RoutedEventArgs e)
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
}
private void closeWindow_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
問題重制步驟:
Step1: 啟動程式
Step2: “Launch Browser Window”
Step3: 在位址欄輸入 http://www.msn.com (其他網址也可以)
Step4: 點選“1. Go/Navigate” button,
Step5: 當網頁加載成功後,點選 “2. Dispose”
Step6: 點選 ”3. Force Garbage Collection”
Step7: 點選“4. Close Window”
重複Step2--Step7 20-25次。
多次測試後的結果如下:
在Step1程式啟動後,記憶體占用在20M左右(不同的機器會有一些差别),

重複Step2--Step7 20-25次之後,程式的記憶體在130M左右,并且長時間等待不釋放。
很不幸運的遇到一個記憶體洩露的問題。
和大多數WPF控件不一樣,WebBrowser控件繼承自HwndHost,使用的是非托管的資源,是以對WebBrowser進行Dispose()操作并不管用。
第一種解決方法:
早前使用過WinForm的WebBrowser控件,不存在記憶體洩露的問題,是以決定使用WinForm的WebBrowser代替WPF的。關于如何在WPF中承載WinForm控件,請參考https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/walkthrough-hosting-a-windows-forms-control-in-wpf
第二種解決方法:
依然使用WPF WebBrowser,但是将第二個頁面(BrowserWindow.xaml)單獨成一個exe。通過主程式去調用,這樣當網頁浏覽完畢後,關閉WebBrowser所在exe,它所關聯的記憶體全部被釋放掉了。如果兩個程式之間需要通信或者交換資料,可以選用WCF/命名管道等方式。
參考連結:
https://stackoverflow.com/questions/2069314/memory-leak-when-using-wpf-webbrowser-control-in-multiple-windows
https://stackoverflow.com/questions/8302933/how-to-get-around-the-memory-leak-in-the-net-webbrowser-control
轉載于:https://www.cnblogs.com/yang-fei/p/7156721.html