У меня возникла проблема, которую я вижу в одном из моих приложений, в невероятно простой образец воспроизведения. Мне нужно знать, есть ли что-то неладное или что-то мне не хватает.
В любом случае, ниже приведен код. Поведение в том, что код работает и неуклонно растет в памяти, пока он не завершится с OutOfMemoryException. Это занимает некоторое время, но поведение заключается в том, что объекты выделяются и не собираются мусором.
Я взял свалки памяти и побежал! gcroot на некоторые вещи, а также использовал ANTS, чтобы выяснить, в чем проблема, но я был на это какое-то время и нуждался в новых глазах.
Этот образец воспроизведения представляет собой простое консольное приложение, которое создает холст и добавляет к нему строку. Он делает это постоянно. Это все, что делает код. Он все время спит, чтобы гарантировать, что процессор не облагается налогом, чтобы ваша система не реагировала (и чтобы там не было странности, когда GC не мог работать).
У кого-нибудь есть мысли? Я пробовал это только с .NET 3.0,.NET 3.5, а также с .NET 3.5 SP1, и такое же поведение произошло во всех трех средах.
Также обратите внимание, что я поместил этот код в проект приложения WPF, а также вызвал код нажатием кнопки, и он также встречается там.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Controls; using System.Windows.Shapes; using System.Windows; namespace SimplestReproSample { class Program { [STAThread] static void Main(string[] args) { long count = 0; while (true) { if (count++ % 100 == 0) { // sleep for a while to ensure we aren't using up the whole CPU System.Threading.Thread.Sleep(50); } BuildCanvas(); } } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] private static void BuildCanvas() { Canvas c = new Canvas(); Line line = new Line(); line.X1 = 1; line.Y1 = 1; line.X2 = 100; line.Y2 = 100; line.Width = 100; c.Children.Add(line); c.Measure(new Size(300, 300)); c.Arrange(new Rect(0, 0, 300, 300)); } } }
ПРИМЕЧАНИЕ. Первый ответ ниже - это бит вне базы, так как я прямо заявил, что такое же поведение происходит во время события нажатия кнопки приложения WPF. Однако я не указал, что в этом приложении я занимаюсь ограниченным числом итераций (скажем, 1000). Выполнение этого способа позволит GC работать, когда вы нажимаете на приложение. Также обратите внимание, что я явно сказал, что я взял дамп памяти и обнаружил, что мои объекты были внедрены через! Gcroot. Я также не согласен с тем, что GC не сможет работать. GC не запускается в основном потоке моего консольного приложения, тем более, что я на двухъядерной машине, что означает, что GC Concurrent Workstation активен. Насос сообщений, однако, да.
Чтобы доказать эту точку, вот версия приложения WPF, которая запускает тест на DispatcherTimer. Он выполняет 1000 итераций во время интервала таймера 100 мс. Более чем достаточно времени для обработки любых сообщений из насоса и низкого уровня использования ЦП.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Shapes; namespace SimpleReproSampleWpfApp { public partial class Window1 : Window { private System.Windows.Threading.DispatcherTimer _timer; public Window1() { InitializeComponent(); _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)] void RunTest() { for (int i = 0; i < 1000; i++) { BuildCanvas(); } } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] private static void BuildCanvas() { Canvas c = new Canvas(); Line line = new Line(); line.X1 = 1; line.Y1 = 1; line.X2 = 100; line.Y2 = 100; line.Width = 100; c.Children.Add(line); c.Measure(new Size(300, 300)); c.Arrange(new Rect(0, 0, 300, 300)); } void _timer_Tick(object sender, EventArgs e) { _timer.Stop(); RunTest(); _timer.Start(); } } }
ПРИМЕЧАНИЕ 2. Я использовал код из первого ответа, и моя память росла очень медленно. Обратите внимание, что 1 мс намного медленнее и меньше итераций, чем мой пример. Вы должны позволить ему работать в течение пары минут, прежде чем вы начнете замечать рост. Через 5 минут оно составляет 46 МБ от начальной точки 30 МБ.
ПРИМЕЧАНИЕ 3: Удаление вызова .Arrange полностью исключает рост. К сожалению, этот вызов очень важен для моего использования, поскольку во многих случаях я создаю PNG файлы с Canvas (через класс RenderTargetBitmap). Без вызова .Arrange это вовсе не макет холста.