WPF中的内存泄漏、计时器

发布于 2024-10-10 04:31:39 字数 5883 浏览 0 评论 0原文

我有这个问题。我使用 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 内存分析器中的值,以下是值:

任务管理器进程:

  • 带有计时器的应用程序
    • 启动后: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();

I have this problem. I develop WPF app with MVVM architecture. As MVVM framework I use Caliburn.Micro and on service injection I use MEF.

In view model class I use Dispatcher Timer which call every 3 seconds service method. This service method send POST request on server and parse HTML response / deserialize JSON string with JSON.NET.

This method return observable dictionary, is type : string, Friend.

Friend class consist only string properties and also one image as BitmapImage type.

Here is service method:

     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>();
        }

In view model class I bind observable dictionary on listbox. In this class I call in timer every 3 seconds service mehod LoadFriend.

I returns fresh data in observable dictionary and with this data I refresh dictionary which is bind on listbox.

Here is view model class:

[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
}

I make a simple test:

  • run app with timer
  • run app without timer
  • with timer but empty logic for
    timer_tick method

I check values in task manager and in ANTS Memory profiler, here are value:

Task manager process:

  • App with timer
    • after start : 40,133K
    • after 5 minutes: 70,261K
    • after 10minutes: 74,288K

  • App without timer
    • after start : 37,488K
    • after 5 minutes: 37,412K
    • after 10minutes: 37,760K

  • App with timer but empty logic
    • after start : 37,474K
    • after 5 minutes:37,340K
    • after 10minutes: 37,476k

ANTS Memory profiler - Privates bytes

  • App with timer
    • after start: 73,132MB
    • after 5 minutes: 97,72MB
    • after 10minutes: 100,12MB

  • App without timer
    • after start : 67,24MB
    • after 5 minutes: 66,21MB
    • after 10minutes: 66,21MB

  • App with timer but empty logic
    • after start : 67,3MB
    • after 5 minutes: 66,32MB
    • after 10minutes:66,41MB

I don’t know that this values are normal. Maybe I have memory leak in app.

My first idea was check images in Friend class, but image is store in BitmapImage class. This class doesn’t implements interface IDisposable, so I can’t call method Dispose on old items in dictionary.

Maximum of privates bytes was 124,12 MB I think its quite enough.

I never handling with memory usage in .NET, I don’t know what can be wrong.

Thank for help, if someone explain me this problem I would be very gratefull him/her.

I think call in my timer garbage collector, but I think it’s goog idea.

Size of one image is 5-10kB. And in dictionary is about 20-30 items.

I download image from web, image uri is 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();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

鱼忆七猫命九 2024-10-17 04:31:40

这是非常宝贵的: 查找基于 WPF 的应用程序中的内存泄漏

BitmapImage 存在问题。

This is invaluable: Finding Memory Leaks in WPF-Based Applications.

There are issues with BitmapImage.

我不咬妳我踢妳 2024-10-17 04:31:40

我在片段中看到的明显的事情是你最终保留了两本字典。

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

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

The obvious thing I see in the snippets is that you end up keeping 2 dictionaries.

There is one in _friends and one in _freshfriends.

Depending on the size of the dictionnary, that could explain some of the difference. As a first step, is there any reason why _freshFriends is no a method local variable?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文