UWP開發入門(十六)——常見的內存泄漏的緣由

  本篇借鑑了同事翔哥的勞動成果,在巨人的肩膀上把稿子又唸了一遍。網絡

  內存泄漏的概念我這裏就不說了,以前《UWP開發入門(十三)——Diagnostic Tool檢查內存泄漏》中提到過,即便有垃圾回收機制,寫C#仍是有可能發生內存泄漏。ide

  通常來講,如下兩種狀況會致使內存泄漏:this

  1. 對象用完了可是沒有釋放資源
  1. 對象自己是作了清理內存的操做,可是對象內部的子對象沒有成功釋放資源

  下面就UWP開發中具體的實例來講明須要避免的寫法spa

  • static/global的對象上註冊了事件
FakeService.Instance.ShowMeTheMoneyEvent += Instance_ShowMeTheMoneyEvent;

  好比咱們有一個底層的FakeService,提供整個APP生命週期的數據和網絡的訪問。假設某個頁面+=了這個FackServiceEvent,在離開頁面時沒有-=掉。那麼該頁面就沒法被垃圾回收。code

合理的作法是在OnNavigatedFrom方法裏,把事件反註冊掉。對象

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);
            FakeService.Instance.ShowMeTheMoneyEvent -= Instance_ShowMeTheMoneyEvent;
        }
  • DispatcherTimer事件未關閉

  這種狀況就屬於對象內部的屬性未能被釋放,假設頁面內部存在Timer對象:blog

    public sealed partial class TimerPage : Page
    {
        private DispatcherTimer Timer { get; set; } = new DispatcherTimer();

        public ArrayList arrayList { get; set; }

        public TimerPage()
        {
            this.InitializeComponent();
            arrayList = new ArrayList(10000000);
            Timer.Tick += Timer_Tick;
            Timer.Interval = TimeSpan.FromSeconds(1);
            Timer.Start();
        }

        private void Timer_Tick(object sender, object e)
        {
            int count = 0;
            int.TryParse(TextBoxTimer.Text, out count);
            count += 1;
            TextBoxTimer.Text = count.ToString();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.Frame.GoBack();
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);
            Timer.Stop();
        }
    }

  若是在離開頁面以前,未調用Timer對象的Stop方法,也未-=Tick事件(這裏Stop方法會自動-=Tick事件)。該頁面就不能正常的回收。繼承

  這裏並非說全部的Event都須要在OnNavigatedFrom方法中-=,例如Control自己的LoadedIsEnabledChanged等事件等並不會形成內存泄漏,反註冊這些事件是爲了不事件的重複觸發。而DispatcherTimer比較特殊,我理解它會把本身加到一個專門維護計時器的隊列中,而後不停的觸發Tick事件,若是沒有Stop-=,就等於Timer一直引用了外部的對象,從而致使頁面自己也沒法回收。接口

  • Data Binding Memory Leak

  這一條在不少的文檔上有所說起,很遺憾我無法經過Diagnostic Tools監測出來具體的泄漏,我猜想多是很小規模的內存泄漏。可是避免的方式很是容易,只要平時寫XAML注意一下就能夠了。生命週期

  會出現問題的寫法是如下兩種:

  1. 未實現INotifyPropertyChanged的對象,而你又想監測Property變化
  2. 未實現INotifyCollectionChanged 接口的集合,而你又想監測Collection變化

  其實很好處理。若是想監測變化,就老老實實繼承對應的接口。若是使用了普通的Property和集合,而且不想監測變化,必定記得Mode = OneTime

  固然若是屬性自己是dependency property,就不存在內存泄漏的狀況了。

        <!--內存泄漏,由於Children集合沒有實現INotifyPropertyChanged來通知Count屬性變化-->
        <TextBlock Text="{Binding ElementName=layoutRoot, Path=Children.Count}" />
        <!--不會內存泄漏,由於ActualWidth是依賴屬性-->
        <TextBlock Text="{Binding ElementName=layoutRoot, Path=ActualWidth}" />
        <!--不會內存泄漏,由於Mode = OneTime-->
        <TextBlock Text="{Binding ElementName=layoutRoot, Path=Children.Count, Mode = OneTime}" />
  • 非託管資源的釋放

  這個都很是熟悉,很少說了。主要是經過using語句,或者在try { … } finally { … }中調用Dispose或者Close方法來釋放非託管資源。

相關文章
相關標籤/搜索