WPF中的内存泄漏、计时器
我有这个问题。我使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是非常宝贵的: 查找基于 WPF 的应用程序中的内存泄漏。
BitmapImage
存在问题。This is invaluable: Finding Memory Leaks in WPF-Based Applications.
There are issues with
BitmapImage
.我在片段中看到的明显的事情是你最终保留了两本字典。
_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?