MVVM更容易内存泄露吗?
由于MVVM是把View, ViewModel, Model緊緊綁定在一起的模式,特別視圖和視圖模型通過實現觀察者模式雙向綁定和NotifyPropertyChanged事件,似乎更加容易造成內存泄露/內存不釋放。網上也有這種說法。真的是這樣的嗎?我們來實際測試一下。
實際測試MVVM是不是容易內存泄露
為了說明問題,我把MVVM搞復雜一點,在ViewModel里面引用一個Singleton單例模式的Service,這個Service定義如下:
1: namespace SilverlightApplication1.Service 2: { 3: public class GlobalService 4: { 5: private static readonly GlobalService Instance = new GlobalService(); 6:? 7: static GlobalService() 8: { 9: } 10:? 11: public static GlobalService GetInstance() 12: { 13: return Instance; 14: } 15: } 16: }寫一個ViewModel,里面引用了Service,用到了ICommand,實現了INotifyPorpertyChanged接口:
1: using System.ComponentModel; 2: using System.Windows.Input; 3: using SilverlightApplication1.Service; 4:? 5: namespace SilverlightApplication1.MVVM 6: { 7: public class ViewModel1 : INotifyPropertyChanged 8: { 9: private GlobalService _injectSingletonService; 10:? 11: public ViewModel1(GlobalService injectSingletonService) 12: { 13: Property1 = "test1"; 14: Command1 = new DelegateCommand(LoadMe, CanLoadMe); 15:? 16: _injectSingletonService = injectSingletonService; 17: } 18:? 19: private string _property1; 20: public string Property1 21: { 22: get { return _property1; } 23: set 24: { 25: _property1 = value; 26:? 27: if (PropertyChanged != null) 28: { 29: PropertyChanged(this, 30: new PropertyChangedEventArgs("Property1")); 31: } 32: } 33: } 34: 35: public ICommand Command1 { get; set; } 36: public event PropertyChangedEventHandler PropertyChanged; 37:? 38: private void LoadMe(object param) 39: { 40: 41: } 42:? 43: private bool CanLoadMe(object param) 44: { 45: return true; 46: } 47: } 48: }來一個視圖View,綁定ViewModel,有個button綁定了ICommand,屬性也綁定了。
1: <UserControl x:Class="SilverlightApplication1.MVVM.View1" 2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4: xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5: xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6: mc:Ignorable="d" 7: d:DesignHeight="300" d:DesignWidth="400"> 8: 9: <Grid x:Name="LayoutRoot" Background="White"> 10: <TextBlock Height="65" HorizontalAlignment="Left" Margin="57,82,0,0" Name="textBlock1" Text="this is view1" VerticalAlignment="Top" Width="224" FontSize="18" /> 11: <Button Content="Button" Command="{Binding Command1}" Height="28" HorizontalAlignment="Left" Margin="55,130,0,0" Name="button1" VerticalAlignment="Top" Width="111" /> 12: <TextBlock Height="28" HorizontalAlignment="Left" Margin="56,173,0,0" Name="textBlock2" Text="{Binding Property1}" VerticalAlignment="Top" Width="114" /> 13: </Grid> 14: </UserControl>這個View1的界面是這樣子的:
View1.xaml.cs代碼:
1: using System.Windows.Controls; 2: using SilverlightApplication1.Service; 3:? 4: namespace SilverlightApplication1.MVVM 5: { 6: public partial class View1 : UserControl 7: { 8: public View1() 9: { 10: InitializeComponent(); 11:? 12: this.DataContext = new ViewModel1(GlobalService.GetInstance()); 13: } 14: } 15: }輔助類DelegateCommand源碼:
1: using System; 2: using System.Windows.Input; 3:? 4: namespace SilverlightApplication1 5: { 6: public class DelegateCommand : ICommand 7: { 8: public event EventHandler CanExecuteChanged; 9:? 10: Func<object, bool> canExecute; 11: Action<object> executeAction; 12: bool canExecuteCache; 13:? 14: public DelegateCommand(Action<object> executeAction, 15: Func<object, bool> canExecute) 16: { 17: this.executeAction = executeAction; 18: this.canExecute = canExecute; 19: } 20:? 21: #region ICommand Members 22:? 23: /// <summary> 24:? 25: /// Defines the method that determines whether the command 26:? 27: /// can execute in its current state. 28:? 29: /// </summary> 30:? 31: /// <param name="parameter"> 32:? 33: /// Data used by the command. 34:? 35: /// If the command does not require data to be passed, 36:? 37: /// this object can be set to null. 38:? 39: /// </param> 40:? 41: /// <returns> 42:? 43: /// true if this command can be executed; otherwise, false. 44:? 45: /// </returns> 46:? 47: public bool CanExecute(object parameter) 48: { 49:? 50: bool tempCanExecute = canExecute(parameter); 51:? 52:? 53:? 54: if (canExecuteCache != tempCanExecute) 55: { 56:? 57: canExecuteCache = tempCanExecute; 58:? 59: if (CanExecuteChanged != null) 60: { 61:? 62: CanExecuteChanged(this, new EventArgs()); 63:? 64: } 65:? 66: } 67:? 68:? 69:? 70: return canExecuteCache; 71:? 72: } 73:? 74:? 75:? 76: /// <summary> 77:? 78: /// Defines the method to be called when the command is invoked. 79:? 80: /// </summary> 81:? 82: /// <param name="parameter"> 83:? 84: /// Data used by the command. 85:? 86: /// If the command does not require data to be passed, 87:? 88: /// this object can be set to null. 89:? 90: /// </param> 91:? 92: public void Execute(object parameter) 93: { 94:? 95: executeAction(parameter); 96:? 97: } 98:? 99: #endregion 100: } 101: }MainPage的代碼:
1: <UserControl x:Class="SilverlightApplication1.MainPage" 2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4: xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5: xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6: mc:Ignorable="d" 7: d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> 8:? 9: <Grid x:Name="LayoutRoot" Background="White"> 10: <Button Content="Add view" Height="36" HorizontalAlignment="Left" Margin="20,31,0,0" Name="button1" VerticalAlignment="Top" Width="115" Click="button1_Click" /> 11: <sdk:TabControl Height="197" HorizontalAlignment="Left" Margin="28,82,0,0" Name="tabControl1" VerticalAlignment="Top" Width="346"> 12: <sdk:TabItem Header="tabItem1" Name="tabItem1"> 13: <Grid /> 14: </sdk:TabItem> 15: </sdk:TabControl> 16: <Button Content="Close view" Height="35" HorizontalAlignment="Left" Margin="148,32,0,0" Name="button2" VerticalAlignment="Top" Width="108" Click="button2_Click" /> 17: </Grid> 18: </UserControl>MainPage界面,主要是在Tab里面打開View1,不斷打開關閉,打開關閉,因為View1是用MVVM模式實現的,看看有內存泄露:
MainPage.xaml.cs,就是測試代碼,正常情況下點擊關閉tab,可能GC不會立即回收內存,這里為了便于測試,手動加了GC.Collect。(正常情況下,不推薦使用GC.Collect())
1: using System; 2: using System.Windows; 3: using System.Windows.Controls; 4: using SilverlightApplication1.MVVM; 5:? 6: namespace SilverlightApplication1 7: { 8: public partial class MainPage : UserControl 9: { 10: public MainPage() 11: { 12: InitializeComponent(); 13: } 14:? 15: private void button1_Click(object sender, RoutedEventArgs e) 16: { 17: var v = new View1(); 18: TabItem t = new TabItem {Content = v, Header = "header " + DateTime.Now.Second.ToString()}; 19: this.tabControl1.Items.Add(t); 20: } 21:? 22: private void button2_Click(object sender, RoutedEventArgs e) 23: { 24: this.tabControl1.Items.RemoveAt(0);//view1, viewModel1并沒有立即釋放,由GC決定何時決定。 25:? 26: System.GC.Collect(); 27: System.GC.WaitForPendingFinalizers(); 28:? 29: //{ 30: // FooContext context = new FooContext(); 31: // context.Load(context.MyQuery); 32: //} 33: } 34: } 35: }?
測試結果:內存泄露和MVVM無關
我的測試結果是內存能夠釋放,沒有內存泄露問題,也就是說MVVM模式和內存泄露無關。那種所謂的MVVM更容易內存泄露的說法沒有什么道理。但不排除你的ViewModel和Model里面有復雜的引用關系,比如你的VIewModel或者Model引用了其他的類,你可能沒有察覺,而那些類可能是Public Static的(是GC Root,不釋放),或者是永遠不釋放的(如MainForm)引用,那就復雜了。由于你的ViewModel被那些不釋放的對象引用著,而你卻不知道,那就是內存泄露了。這和MVVM沒有關系。
?
深入思考和繼續閱讀
通常.NET程序的內存泄露原因:
- Static references
- Event with missing unsubscription
- Static event with missing unsubscription
- Dispose method not invoked
- Incomplete Dispose method
有關如何避免.NET程序的內存泄露,請仔細閱讀MSDN這兩篇文章,詳細講述了<如何檢測.NET程序內存泄露>以及<如何寫高性能的托管程序>
- How to detect and avoid memory and resources leaks in .NET applications
- Writing High-Performance Managed Applications : A Primer
有關.NET的自動內存管理機制、GC機制,垃圾回收原理等深層次內容,請仔細閱讀下面的內容:
- 買書《CLR Via C#(3rd Edition)》,里面有《Memory Management》這一章專門講述了.NET CLR的自動內存管理和垃圾回收機制
- CodeProject上的文章《Memory Management Misconceptions》有助你深入理解Root, Generation 0, 1…
@:Mainz?→?http://www.cnblogs.com/Mainz?
?: 博文是本人當時的學習筆記及知識整理,由于自身局限錯誤在所難免,敬請斧正. 博文中源碼只作為例子學習參考之用,不保證能運行,對后果不負任何責且無任何質保,如有不明請給我留言?
?: 本文版權屬于博客園和本人,版權基于署名 2.5 中國大陸許可協議發布,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接和署名Mainz(包含鏈接),不得刪節,否則保留追究法律責任的權利。
轉載于:https://www.cnblogs.com/Areas/archive/2011/09/23/2186621.html
總結
以上是生活随笔為你收集整理的MVVM更容易内存泄露吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 推荐几款国产快速启动工具软件 总有一款适
- 下一篇: windows计算器_计算Windows