Утечка памяти, таймер в WPF - PullRequest
0 голосов
/ 02 января 2011

У меня есть эта проблема.Я разрабатываю приложение WPF с архитектурой MVVM.В качестве фреймворка MVVM я использую Caliburn.Micro, а при внедрении сервиса я использую MEF.

В классе модели представления я использую таймер Dispatcher, который вызывает каждые 3 секунды метод обслуживания.Этот сервисный метод отправляет POST-запрос на сервер и анализирует HTML-ответ / десериализует строку JSON с JSON.NET.

Этот метод возвращает наблюдаемый словарь, это тип: string, Friend.

Класс Friend состоит только из строковых свойств и одного изображения в виде типа BitmapImage.

ЗдесьСервисный метод:

     public MyObservableDictionary<string, Friend> LoadFriends(Account account)
        {

            var friends = new MyObservableDictionary<string, Friend>();
            var sortedFriends = new MyObservableDictionary<string, Friend>();

            const string allData = "&allData=1";

            var htmlStringResult = new StringBuilder();

            htmlStringResult = "GET HTML RESPOSE"

            if (htmlStringResult.Length > 3 && htmlStringResult.ToString() != "false")
            {
                try
                {
                    var jsonString = new StringBuilder();
                    jsonString.Append(htmlStringResult.Replace(@"s_", "m_"));

                    var friendsAsStringArray = JsonConvert.DeserializeObject<MyObservableDictionary<string, string[]>>(jsonString.ToString());

                    foreach (var friend in friendsAsStringArray)
                    {
                        var item = new KeyValuePair<string, Friend>(friend.Key, new Friend
                        {
                            IdUser = friend.Value[0],
                            Nick = friend.Value[1],
                            SefNick = friend.Value[1],
                            Status = int.Parse(friend.Value[2]),
                            Photo = friend.Value[3],
                            Sex = int.Parse(friend.Value[4]),
                            IsFriend = Convert.ToBoolean(int.Parse(friend.Value[5]) * -1)
                        });
                        friends.Add(item);
                    }


//sort item in dictionary  A-Z
                    var query = friends.OrderByDescending(f => f.Value.Status).ThenBy(f => f.Value.Nick);

                    foreach (var keyValuePair in query)
                    {
                        sortedFriends.Add(keyValuePair.Key, keyValuePair.Value);
                    }

                    //CLEAN OLD DATA
                    friends.Clear();
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                return sortedFriends;
            }
            return new MyObservableDictionary<string, Friend>();
        }

В представлении модели класса я привязываю наблюдаемый словарь к списку.В этом классе я вызываю таймер каждые 3 секунды. Сервисный метод LoadFriend .

Я возвращаю свежие данные в наблюдаемый словарь, и этими данными я обновляю словарь, который связывается со списком.

Вот вид модели класса:

[Export(typeof(IMessengerViewModel))]
public class MessengerViewModel : Screen, IViewModelIdentity,
    IMessengerViewModel, IHandle<Rp>, IHandle<string>
{
    private IPokecService _service;

    private MyObservableDictionary<string, Friend> _friends;
    private MyObservableDictionary<string, Friend> _freshFriends;

    private DispatcherTimer _dispatcherTimer;

    [ImportingConstructor]
    public MessengerViewModel(IPokecService service)
    {
        _service = service;
        _dispatcherTimer = new DispatcherTimer();
        _dispatcherTimer.Tick += DispatcherTimer_Tick;
        _dispatcherTimer.Interval = TimeSpan.FromSeconds(3);
        _dispatcherTimer.Start();

    }


    #region Timer

    /// <summary>
    /// Refresh contact list, check for new messages
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="eventArgs"></param>
    private void DispatcherTimer_Tick(object sender, EventArgs eventArgs)
    {
        //retrieve new data from server
        _freshFriends = _service.LoadFriends(Account);

        _friends.Clear();


        //refresh dictionary
        foreach (var freshFriend in _freshFriends)
        {
            _friends.Add(freshFriend);
        }
    }

    #endregion
}

Я делаю простой тест:

  • запуск приложения с таймером
  • запуск приложениябез таймера
  • с таймером, но пустой логикой для метода timer_tick

Я проверяю значения в диспетчере задач и в профилировщике памяти ANTS, вот значения:

Процесс диспетчера задач:

  • Приложение с таймером
    • после запуска: 40 133 К
    • через 5 минут: 70 261 К
    • через 10 минут: 74 288 К

  • Приложение без таймера
    • после запуска: 37 488 К
    • через 5 минут: 37 412 К
    • через 10 минут: 37 760 К

  • Приложение с таймером, но с пустой логикой
    • после запуска: 37 474 К
    • через 5 минут: 37,340K
    • через 10 минут: 37,476k

ANTS Профилировщик памяти - Частные байты

  • Приложение с таймером
    • после запуска: 73,132 МБ
    • через 5 минут: 97,72 МБ
    • через 10 минут: 100,12 МБ

  • Приложение без таймера
    • после запуска: 67,24 МБ
    • через 5 минут: 66,21 МБ
    • через 10 минут: 66,21 МБ

  • Приложение с таймером, но с пустой логикой
    • после запуска: 67,3 МБ
    • через 5 минут: 66,32MB
    • через 10 минут: 66,41MB

Я не знаю, что это нормальные значения.Возможно, у меня утечка памяти в приложении.

Моей первой идеей была проверка изображений в классе Friend, но изображение сохраняется в классе BitmapImage.Этот класс не реализует интерфейс IDisposable, поэтому я не могу вызвать метод Dispose для старых элементов в словаре.

Максимальное количество приватных байтов составило 124,12 МБ. Я думаю, этого вполне достаточно.

Я никогда не обращался с использованием памяти в .NET, я не знаю, что может быть не так.

Спасибо за помощь, если кто-то объяснит мне эту проблему, я буду очень ему признателен.

Я думаю, что позвоните в мой таймер сборщик мусора, но я думаю, что это идея goog.

Размер одного изображения составляет 5-10 КБ.И в словаре около 20-30 наименований.

Я загружаю изображение из Интернета, изображение - это URL.

 public BitmapImage ProfilePhoto ...{ get; set; }

            private BitmapImage CreateProfilePhoto()
            {
                var img = new BitmapImage();
                img.BeginInit();

                img.UriSource = Photo == "0" ? DefaultPhoto.GetDefaultPhoto(Sex) : new Uri(PhotoURL, UriKind.Absolute);

                img.EndInit();

                return img;
            }

    ....

            ProfilePhoto = CreateProfilePhoto();

Ответы [ 2 ]

1 голос
/ 11 октября 2011

Очевидная вещь, которую я вижу в фрагментах, это то, что вы в конечном итоге держите 2 словаря.

Один из них _ffriends и один в _freshfriends.

В зависимости от размера словаря, это может объяснить некоторые различия. В качестве первого шага, есть ли причина, по которой _freshFriends не является локальной переменной метода?

1 голос
/ 02 января 2011

Это бесценно: Поиск утечек памяти в приложениях на основе WPF .

Есть проблемы с BitmapImage.

...