【问题标题】:Memory leak , timer in WPF内存泄漏,WPF中的计时器
【发布时间】:2011-01-02 14:55:03
【问题描述】:

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

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

此方法返回可观察字典,类型为:字符串,朋友。

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,133K
    • 5 分钟后:70,261K
    • 10 分钟后:74,288K

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

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

ANTS 内存分析器 - 私有字节

  • 带计时器的应用程序
    • 启动后:73,132MB
    • 5 分钟后:97,72MB
    • 10 分钟后:100,12MB

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

  • 具有计时器但逻辑为空的应用
    • 启动后:67.3MB
    • 5 分钟后:66.32MB
    • 10 分钟后:66,41MB

我不知道这个值是否正常。也许我的应用程序有内存泄漏。

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

私有字节的最大值为 124,12 MB,我认为这已经足够了。

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

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

我想调用我的计时器垃圾收集器,但我认为这是一个很好的主意。

一张图片的大小为 5-10kB。在字典中大约有 20-30 项。

我从网上下载图片,图片 uri 是 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();

【问题讨论】:

  • 如果你已经有一个内存分析器,为什么不使用它来查看“泄漏”了什么?使用 ANTS 拍摄快照并查看创建了哪些对象,但未销毁哪些对象并得出结论。
  • 我试了一下,但我找不到内存泄漏.. :(

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


【解决方案1】:

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

BitmapImage 存在问题。

【讨论】:

  • 感谢您的想法。我从网上下载图像,但我使用.NET4.0 ...在这篇文章中谈到了3.5。
【解决方案2】:

我在 sn-ps 中看到的显而易见的事情是您最终保留了 2 个字典。

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

根据字典的大小,这可以解释一些差异。作为第一步,_freshFriends 不是方法局部变量有什么原因吗?

【讨论】:

    猜你喜欢
    • 2011-02-21
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-05
    相关资源
    最近更新 更多