Silverlight + MVVM + Bindings = утечки памяти?

До сих пор мое тестирование показано, что все стандартные подходы, примеры и платформы, использующие шаблон MVVM в silverlight, страдают от огромной проблемы: массивных утечек памяти, которые препятствуют сборке мусора для виртуальных машин.

Очевидно, что это громадное и нелепое утверждение— так что я ожидаю, что кто-то даст очевидный ответ, почему и где я ошибаюсь :)

Шаги для воспроизведения просты:

  • Привяжите свою модель представления к представлению, установив контекст данных представлений для виртуальной машины (предположим, что модель представления использует INotifyPropertyChanged для поддержки привязки данных)
  • Привяжите элемент пользовательского интерфейса к свойству модели представления, например:

  • Каким-то образом используйте привязку (например, просто введите в текстовое поле).

Это создает цепочку ссылок, которая простирается от корня до BindingExpression и вашей модели представления. Затем вы можете удалить представление из дерева пользовательского интерфейса, а также все ссылки на виртуальную машину, однако виртуальная машина никогда не будет удалена сборщиком мусора благодаря цепочке ссылок rootBindingExpressionVM.

Я создал два примера, иллюстрирующих проблему. У них есть кнопка для создания нового представления/модели представления (которая должна сбрасывать все ссылки на старые) и кнопка, которая вызывает сборку мусора и сообщает о текущем использовании памяти.

Пример 1 представляет собой сверхупрощенный микропример калиберна. Пример 2 не использует никаких фреймворков и просто иллюстрирует проблему самым простым способом, который я мог придумать.

Пример 1

Пример 2

Для тех, кто хочет помочь, но не хочет загружать примеры проектов, вот код примера 2. Начнем с модели представления под названием FooViewModel:

 public class FooViewModel : INotifyPropertyChanged
{
    string _fooText;

    public string FooText
    {
        get { return _fooText; }
        set
        {
            _fooText = value;
            NotifyPropertyChanged("FooText");
        }
    }

    private byte[] _data;
    public FooViewModel()
    {
        _data = new byte[10485760]; //use up 10mb of memory
    }



    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Он просто предоставляет строковое свойство с именем FooText, которое мы также привяжем. INotifyPropertyChanged необходим для облегчения привязки.

Затем у нас есть представление с именем FooView, которое представляет собой пользовательский элемент управления, содержащий:


    
        
        
    

(пространства имен опущены для краткости)

Важным моментом здесь является текстовое поле, которое привязано к свойству FooText. Конечно, нам нужно установить контекст данных, который я решил сделать в отделенном коде, а не вводить ViewModelLocator:

public partial class FooView : UserControl
{
    public FooView()
    {
        InitializeComponent();
        this.DataContext = new FooViewModel();
    }
}

MainPage выглядит следующим образом:



    

со следующим кодом:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        myContent.Content = new FooView();
    }

    private void Button2_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Memory in use after collection: " + (GC.GetTotalMemory(true) / 1024 / 1024).ToString() + "MB");
    }

Примечание. воспроизведите проблему, обязательно введите что-нибудь в текстовое поле, так как я считаю, что выражение привязки не создается до тех пор, пока оно не понадобится.

Стоит отметить, что эта статья базы знанийможет быть связана с этим, однако я не уверен, поскольку обходной путь «метода 2», похоже, не имеет эффекта, а цепочка ссылок, похоже, не соответствовать.

Кроме того, я не уверен, что это имеет значение, но я использовал CLR Profilerдля диагностики причины.

Обновление:

Если кто-то хочет протестировать и сообщить о своих выводах в комментариях, я размещаю приложение Silverlight через Dropbox здесь: Размещенный пример.Чтобы воспроизвести: нажмите верхнюю кнопку, введите что-нибудь, нажмите верхнюю кнопку, введите что-нибудь, нажмите верхнюю кнопку. Затем нажмите кнопку. Если он сообщает об использовании 10 МБ (или, возможно, о каком-то другом объеме, который не увеличивается), вы не испытываете утечки памяти.

На данный момент проблема возникает на ВСЕХ наших компьютерах для разработки, а именно ThinkPad w510 (43192RU) с 12 ГБ оперативной памяти и 64-разрядной ОС Win 7 Enterprise. Сюда входят некоторые, на которых не установлены инструменты разработки. Возможно, стоит отметить, что они работают на рабочей станции VMWare.

Проблема НЕ возникает на других машинах, которые я пробовал, включая несколько домашних ПК и других ПК в офисе. Мы несколько исключили версии SL, объем памяти и, возможно, vmware. До сих пор не установил причину.

9
задан Shaun Rowan 18 May 2012 в 17:56
поделиться