WPF 屬性系統 依賴屬性之記憶體佔用分析

返回主頁 一天兩天三天發表於2015-03-28

關於WPF的屬性系統園子內有不少這方面的文章。裡面大都提到了WPF依賴屬性的在記憶體方面的優化。但是裡面大都一筆帶過。那麼WPF到底是怎麼樣節約記憶體的。我們通過WPF屬性和普通的CLR屬性對比來看一下WPF屬性在節約記憶體方面的優勢在哪裡。

普通的CLR屬性

public partial class WindowMemory : Window
{
   Student0 stu;
   public WindowMemory()
  {
    InitializeComponent();
    List<Student0> list = new List<Student0>();
    for (int i = 0; i < 10000000; i++)
    {
    stu = new Student0();
    list.Add(stu);
    }
  }
}
public class Student0
    {
        public double Name { get; set; }
        public double Name1 { get; set; }
        public double Name2 { get; set; }
        public double Name3 { get; set; }
        public double Name4 { get; set; }
        public double Name5 { get; set; }
        public double Name6 { get; set; }
        public double Name7 { get; set; }
        public double Name8 { get; set; }
        public double Name9 { get; set; }
        public double Name10 { get; set; }
    }

我們宣告一個Student0類,裡面放入十個屬性。然後new 一千萬個student 的例項載入到記憶體中。在工作管理員中看一下記憶體佔用。

我們看到程式大概佔用了一個G的記憶體。計算一下。因為c#中的屬性是通過get set方法對一個私有欄位的封裝,也就是說這個類裡面有十個double型別的私有欄位。double型別佔8個位元組。一兆是1048576個位元組,131072個double型別。一千萬個double大概佔用76兆的記憶體。我們這兒宣告瞭十個也就是760兆。另外還有student物件佔用的記憶體。所以這兒程式佔用記憶體大概是一個G;

依賴屬性

public class Student0 : DependencyObject
    {
        public double Name
        {
            get
            {
                return (double)GetValue(NameProperty);
            }
            set
            {
                SetValue(NameProperty, value);
            }
        }
        public double Name1
        {
            get
            {
                return (double)GetValue(Name1Property);
            }
            set
            {
                SetValue(Name1Property, value);
            }
        }
        public double Name2
        {
            get
            {
                return (double)GetValue(Name2Property);
            }
            set
            {
                SetValue(Name2Property, value);
            }
        }
        public double Name3
        {
            get
            {
                return (double)GetValue(Name3Property);
            }
            set
            {
                SetValue(Name3Property, value);
            }
        }

        public double Name4
        {
            get
            {
                return (double)GetValue(Name4Property);
            }
            set
            {
                SetValue(Name4Property, value);
            }
        }
        public double Name5
        {
            get
            {
                return (double)GetValue(Name5Property);
            }
            set
            {
                SetValue(Name5Property, value);
            }
        }
        public double Name6
        {
            get
            {
                return (double)GetValue(Name6Property);
            }
            set
            {
                SetValue(Name6Property, value);
            }
        }

        public double Name7
        {
            get
            {
                return (double)GetValue(Name7Property);
            }
            set
            {
                SetValue(Name7Property, value);
            }
        }
        public double Name8
        {
            get
            {
                return (double)GetValue(Name8Property);
            }
            set
            {
                SetValue(Name8Property, value);
            }
        }
        public double Name9
        {
            get
            {
                return (double)GetValue(Name9Property);
            }
            set
            {
                SetValue(Name9Property, value);
            }
        }
        public double Name10
        {
            get
            {
                return (double)GetValue(Name10Property);
            }
            set
            {
                SetValue(Name10Property, value);
            }
        }public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name1Property = DependencyProperty.Register("Name1", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name2Property = DependencyProperty.Register("Name2", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name3Property = DependencyProperty.Register("Name3", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name4Property = DependencyProperty.Register("Name4", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name5Property = DependencyProperty.Register("Name5", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name6Property = DependencyProperty.Register("Name6", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name7Property = DependencyProperty.Register("Name7", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name8Property = DependencyProperty.Register("Name8", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name9Property = DependencyProperty.Register("Name9", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name10Property = DependencyProperty.Register("Name10", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
    }

我們把student類修改為依賴物件,在裡面實現十個依賴屬性,此時來檢視一下記憶體佔用

此時只有三百多兆的記憶體佔用。那麼WPF的屬性到底是如何節約記憶體的呢。因為CLR屬性是在例項宣告的時候就分配好了記憶體空間的。所以就算例項裡面沒有寫入值,或者仍然是預設值,仍然會分配好記憶體空間。但是WPF的依賴屬性不同。wpf的依賴屬性是如下宣告的

public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));

注意上面的這條語句才是依賴屬性的宣告。而類似下面這樣的是我們通過clr屬性對依賴屬性NameProperty進行了包裝,使我們訪問起來更方便。就想普通的屬性那樣。

public double Name
{
      get
      {
          return (double)GetValue(NameProperty);
      }
      set
     {
         SetValue(NameProperty, value);
     }
}

也就是說我們其實可以直接這樣來宣告Student0物件

public class Student0 : DependencyObject
    {
        public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name1Property = DependencyProperty.Register("Name1", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name2Property = DependencyProperty.Register("Name2", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name3Property = DependencyProperty.Register("Name3", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name4Property = DependencyProperty.Register("Name4", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name5Property = DependencyProperty.Register("Name5", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name6Property = DependencyProperty.Register("Name6", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name7Property = DependencyProperty.Register("Name7", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name8Property = DependencyProperty.Register("Name8", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name9Property = DependencyProperty.Register("Name9", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
        public static readonly DependencyProperty Name10Property = DependencyProperty.Register("Name10", typeof(double), typeof(Student0), new PropertyMetadata((double)55.55));
    }

然後通過setvalue,getvalue來存取值

注意其實依賴屬性的宣告,在這裡或者用註冊來形容更貼切,只是一個入口點。也就是我們平常常說的單例模式。屬性的值其實都放在依賴物件的一個雜湊表裡面。這裡資料很多,大家隨便找下就可以找到。

所以依賴屬性正在節約記憶體就在於這兒的依賴屬性是一個static readonly 屬性。所以不需要在物件每次例項化的時候都分配相關屬性的記憶體空間,而是提供一個入口點。

知道了這些我們再看一個沒什麼實際意義的例子。將student0物件修改如下。

public class Student0
    {
        public double Name { get { return 1.00; } set { Name = 1.00; } }
        public double Name1 { get { return 1.00; } set { Name1 = 1.00; } }
        public double Name2 { get { return 1.00; } set { Name2 = 1.00; } }
        public double Name3 { get { return 1.00; } set { Name3 = 1.00; } }
        public double Name4 { get { return 1.00; } set { Name4 = 1.00; } }
        public double Name5 { get { return 1.00; } set { Name5 = 1.00; } }
        public double Name6 { get { return 1.00; } set { Name6 = 1.00; } }
        public double Name7 { get { return 1.00; } set { Name7 = 1.00; } }
        public double Name8 { get { return 1.00; } set { Name8 = 1.00; } }
        public double Name9 { get { return 1.00; } set { Name9 = 1.00; } }
        public double Name10 { get { return 1.00; } set { Name10 = 1.00; } }
    }

此時程式只佔用了不到200兆記憶體。因為屬性的本質其實就是一個get set 方法。而方法是不需要例項化的,只需要一個指標指向就可以了。這兒我沒沒有在屬性get;set;這種簡化寫法下宣告的私有欄位。所以沒有了這些私有欄位佔用的記憶體。記憶體佔用大大減少。

仔細對比我們就會發現。wpf的屬性系統真的沒有特別設計來優化記憶體。只是這種提供入口點的方式順帶就減少了記憶體的佔用。

相關文章