内存泄漏,WPF中的计时器

时间:2011-01-02 14:55:03

标签: .net memory-leaks garbage-collection timer memory-management

我有这个问题。我用MVVM架构开发WPF应用程序。作为MVVM框架,我使用Caliburn.Micro并在服务注入时使用MEF。

在视图模型类中,我使用Dispatcher Timer,它调用每3秒的服务方法。此服务方法在服务器上发送POST请求,并使用JSON.NET解析HTML响应/反序列化JSON字符串。

此方法返回可观察字典,类型为: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内存分析器中的值,这里是值:

任务管理器流程:

  • 带定时器的App
      开始后
    • :40,133K
    • 5分钟后:70,261K
    • 10分钟后:74,288K

  • 没有计时器的应用程序
    • 开始后:37,488K
    • 5分钟后:37,412K
    • 10分钟后:37,760K

  • App有计时器但空逻辑
    • 开始后:37,474K
    • 5分钟后
    • :37,340K
    • 10分钟后:37,476k

ANTS内存探查器 - 私有字节

  • 带定时器的App
      开始后
    • :73,132MB
    • 5分钟后:97,72MB
    • 10分钟后:100,12MB

  • 没有计时器的应用程序
    • 开始后:67,24MB
    • 5分钟后:66,21MB
    • 10分钟后:66,21MB

  • App有计时器但空逻辑
    • 开始后:67,3MB
    • 5分钟后:66,32MB
    • 10分钟后:66,41MB

我不知道这个值是正常的。也许我在app中有内存泄漏。

我的第一个想法是在Friend类中检查图像,但是图像存储在BitmapImage类中。这个类没有实现接口IDisposable,所以我不能在字典中的旧项目上调用Dispose方法。

最大私有字节数为124,12 MB我觉得它已经足够了。

我从不处理.NET中的内存使用情况,我不知道会出现什么问题。

感谢您的帮助,如果有人向我解释这个问题,我将非常感激他/她。

我想在我的计时器垃圾收集器中调用,但我认为这是goog的想法。

一张图像的尺寸为5-10kB。在词典中约有20-30项。

我从网上下载图片,图片uri是网址。

 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 个答案:

答案 0 :(得分:1)

这非常宝贵:Finding Memory Leaks in WPF-Based Applications

BitmapImage存在问题。

答案 1 :(得分:1)

我在片段中看到的显而易见的事情是你最终会保留2个词典。

_friends中有一个,_freshfriends中有一个。

根据字典的大小,这可以解释一些不同之处。作为第一步,是否有任何理由为_freshFriends不是方法局部变量?