我已经将我在我的一个应用程序中看到的一个问题归结为一个非常简单的复制样本。我需要知道是否有什么不对劲或我错过了什么。
无论如何,下面是代码。行为是代码在内存中运行并稳定增长,直到它因 OutOfMemoryException 而崩溃。这需要一段时间,但行为是正在分配对象并且没有被垃圾收集。
我已经进行了内存转储并在一些事情上运行了 !gcroot 并使用 ANTS 来找出问题所在,但我已经研究了一段时间并且需要一些新的眼光。
此复制示例是一个简单的控制台应用程序,它创建一个 Canvas 并向其添加一个 Line。它不断地这样做。这就是代码所做的全部。它时不时地休眠以确保 CPU 不会因为过度使用而导致系统无响应(并确保 GC 无法运行时不会出现异常情况)。
有人有什么想法吗?我仅使用 .NET 3.0、.NET 3.5 和 .NET 3.5 SP1 进行了尝试,并且在所有三个环境中都发生了相同的行为。
另请注意,我也将此代码放入了 WPF 应用程序项目中,并在单击按钮时触发了代码,它也发生在那里。
使用系统;
使用 System.Collections.Generic;
使用 System.Linq;
使用 System.Text;
使用 System.Windows.Controls;
使用 System.Windows.Shapes;
使用 System.Windows;
命名空间 SimplestReproSample
{
课堂节目
{
[STA线程]
静态无效主要(字符串 [] 参数)
{
长计数 = 0;
而(真)
{
如果(计数++ % 100 == 0)
{
// 休眠一段时间以确保我们没有用完整个 CPU
System.Threading.Thread.Sleep(50);
}
构建画布();
}
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
私有静态无效 BuildCanvas()
{
画布 c = 新画布();
线线 = 新线();
线.X1 = 1;
线.Y1 = 1;
线.X2 = 100;
线.Y2 = 100;
line.Width = 100;
c.Children.Add(line);
c.Measure(新尺寸(300, 300));
c.Arrange(新矩形(0, 0, 300, 300));
}
}
}
注意:下面的第一个答案有点偏离基础,因为我已经明确指出在 WPF 应用程序的按钮单击事件期间会发生同样的行为。但是,我没有明确说明在那个应用程序中我只进行了有限次数的迭代(比如 1000 次)。这样做可以让 GC 在您单击应用程序时运行。另请注意,我明确表示我已经进行了内存转储,并发现我的对象是通过 !gcroot 植根的。我也不同意 GC 将无法运行。GC 不在我的控制台应用程序的主线程上运行,特别是因为我在双核机器上,这意味着 Concurrent Workstation GC 处于活动状态。然而,消息泵,是的。
为了证明这一点,这里有一个在 DispatcherTimer 上运行测试的 WPF 应用程序版本。它在 100 毫秒的计时器间隔内执行 1000 次迭代。有足够的时间来处理来自泵的任何消息并保持较低的 CPU 使用率。
使用系统;
使用 System.Collections.Generic;
使用 System.Linq;
使用 System.Text;
使用 System.Windows;
使用 System.Windows.Controls;
使用 System.Windows.Shapes;
命名空间 SimpleReproSampleWpfApp
{
公共部分类 Window1:窗口
{
私有 System.Windows.Threading.DispatcherTimer _timer;
公共窗口1()
{
初始化组件();
_timer = new System.Windows.Threading.DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(100);
_timer.Tick += new EventHandler(_timer_Tick);
_timer.Start();
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
无效运行测试()
{
for (int i = 0; i < 1000; i++)
{
构建画布();
}
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
私有静态无效 BuildCanvas()
{
画布 c = 新画布();
线线 = 新线();
线.X1 = 1;
线.Y1 = 1;
线.X2 = 100;
线.Y2 = 100;
line.Width = 100;
c.Children.Add(line);
c.Measure(新尺寸(300, 300));
c.Arrange(新矩形(0, 0, 300, 300));
}
void _timer_Tick(对象发送者,EventArgs e)
{
_timer.Stop();
运行测试();
_timer.Start();
}
}
}
注意2:我使用了第一个答案中的代码,我的记忆增长非常缓慢。请注意,1ms 比我的示例慢得多,迭代次数也少得多。在开始注意到增长之前,您必须让它运行几分钟。5 分钟后,它从 30MB 的起点变为 46MB。
注意3:删除对 .Arrange 的调用完全消除了增长。不幸的是,该调用对我的使用非常重要,因为在许多情况下,我是从 Canvas(通过 RenderTargetBitmap 类)创建 PNG 文件。如果没有调用 .Arrange 它根本不会布局画布。