【发布时间】:2019-09-05 19:25:24
【问题描述】:
我有以下代码,它会导致内存泄漏。
问题是任务,当我删除它时,一切都很好,并且 View 以及 ViewModel 都被 GCed。似乎任务保留了对 UpdateTimeDate 的引用,因此也保留了 ViewModel。我尝试了各种方法,但都没有奏效,希望有人知道或解释为什么会这样。
public class HeaderViewModel : Observable, IDisposableAsync
{
public HeaderViewModel (IDateTimeProvider dateTimeProvider)
{
TokenSource = new CancellationTokenSource();
ATask = Task.Run(
async () =>
{
while(!TokenSource.Token.IsCancellationRequested)
{
UpdateTimeData();
await Task.Delay(800);
}
IsDisposed = true;
},
TokenSource.Token);
UpdateTimeData();
void UpdateTimeData()
{
TimeText = dateTimeProvider.Now.ToString("HH:mm:ss");
DateText = dateTimeProvider.Now.ToString("dd.MM.yyyy");
}
}
public CancellationTokenSource TokenSource { get; set; }
public bool IsDisposed { get; set; }
public string TimeText
{
get => Get<string>();
set => Set(value);
}
public string DateText
{
get => Get<string>();
set => Set(value);
}
private Task ATask { get; set; }
public async Task Dispose()
{
TokenSource.Cancel();
while(!IsDisposed)
{
await Task.Delay(50);
}
TokenSource.Dispose();
ATask.Dispose();
ATask = null;
TokenSource = null;
}
}
这是基于 Timer 的解决方案,它也会导致内存泄漏:
public class HeaderViewModel : Observable, IDisposableAsync
{
public HeaderViewModel(IDateTimeProvider dateTimeProvider)
{
DateTimeProvider = dateTimeProvider;
ATimer = new Timer(800)
{
Enabled = true
};
UpdateTimeData(this, null);
ATimer.Elapsed += UpdateTimeData;
}
public string TimeText
{
get => Get<string>();
set => Set(value);
}
public string DateText
{
get => Get<string>();
set => Set(value);
}
public bool IsDisposed { get; set; }
private IDateTimeProvider DateTimeProvider { get; }
private Timer ATimer { get; }
public async Task Dispose()
{
ATimer.Stop();
await Task.Delay(1000);
ATimer.Elapsed -= UpdateTimeData;
ATimer.Dispose();
IsDisposed = true;
}
private void UpdateTimeData(object sender, ElapsedEventArgs elapsedEventArgs)
{
TimeText = DateTimeProvider.Now.ToString("HH:mm:ss");
DateText = DateTimeProvider.Now.ToString("dd.MM.yyyy");
}
}
【问题讨论】:
-
您是否考虑过改用
Timer? -
是的,我尝试了一个计时器。它有完全相同的问题。我知道这段特定的代码会导致内存泄漏,因为我使用 dotMemory 对其进行了分析,并且还有一个多次实例化 View 的测试。除非删除任务/计时器,否则内存会不断增长。
-
您正在捕获隐含在 lambda 中的 ViewModel (this) 实例。尝试改用 WeakRefrence。
-
现在包含在内。我真的不明白为什么两者都不起作用。
-
泄漏有多大(多长时间内有多少内存)?