Я обнаружил утечку памяти в своей программе и после долгих исследований смог изолировать проблему и создал этот образец:
Целевая платформа: .NET Framework 4.7.2
AppFormMain.cs:
private void MainForm_Shown(object sender, EventArgs e)
{
for (int i = 0; i < 90000; i++)
{
Console.WriteLine("i " + i);
AppMain.GetInstance().memoryLeakTest();
}
GC.Collect();
GC.WaitForPendingFinalizers(); GC.Collect(); // ADDED ON [UPDATE 2]
}
AppMain.cs:
protected static AppMain appInstance;
public AppMain()
{
SetInstance(this);
}
public void SetInstance(AppMain value)
{
appInstance = value;
}
public static AppMain GetInstance()
{
return appInstance;
}
public void memoryLeakTest()
{
ToolStripMenuItem item = new ToolStripMenuItem();
item.Text = "Create";
item.Image = System.Resources.create; // png 16x16 47.6KB
item.Dispose(); // ADDED ON [UPDATE 1]
}
Если я удаляю цикл в MainForm_Shown (), я получаю примерно 93 МБ ram. Если я зацикливаю 90000 раз, максимальный ram составляет 1,1 ГБ во время цикла, а после сборщика мусора я заканчиваю использование 180 МБ, и после этого он не снижается.
Если я удалю строку «item.Image = ...», использование будет менее агрессивным, но у меня все еще будет утечка памяти около 150 МБ.
Почему эта утечка памяти? переменная в memoryLeakTest () даже не используется ....
Цикл 90000 предназначен только для тестирования, представляющего многочасовое использование. При использовании приложения через 24 часа оно использует 2 ГБ оперативной памяти только потому, что требуется обновление в пользовательском интерфейсе, при загрузке используется только 100 МБ.
Любая помощь будет оценена по достоинству. Я не могу понять, как отказаться от этого или даже почему это приложение.
Извините, если я забываю важную информацию, это моя первая программа на C#.
Спасибо.
[ОБНОВЛЕНИЕ 1]
Добавлен Dispose()
, как было предложено в комментариях, помог, но не полностью решил проблему. Значения использования памяти в приведенном выше тексте были обновлены с учетом новых результатов.
[ОБНОВЛЕНИЕ 2]
Добавлен GC.WaitForPendingFinalizers(); GC.Collect();
, как предлагалось в комментариях. Это помогло уменьшить со 180 МБ до 130 МБ. По-прежнему остается разница в ~ 40 МБ по сравнению с базовым уровнем ~ 90 МБ без цикла.
Следует избавиться от пункта меню: Методы ToolStripMenuItem
int i = 0; i < 90000
- это много ToolStripMenuItem
, чего вы пытаетесь достичь?
Пытаюсь добавить GC.Collect (); после for () и ничего не сделал. Я также добавил item.Dispose (); в конце memoryLeakTest (), и это снизило использование памяти примерно до 180 МБ. 90000 - это действительно много объектов, я пытаюсь воспроизвести проблему использования оперативной памяти 2 ГБ после 24 часов использования только потому, что мне нужно перестроить пользовательский интерфейс приложения ...
Dispose
, должны быть удалены, когда вы закончите с ним. В зависимости от того, что на самом деле делает Resources.create
, у вас может быть еще одна утечка.
хорошо, Dispose помог, но не устранил проблему полностью. Файл Resources.created - это просто png 16x16 47.6KB из ресурсов проекта.
Я обновил исходный пост всей этой информацией
Попробуйте GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
Это помогло, уменьшилось со 180 МБ до 130 МБ. Еще есть разница в ~ 40мб. Я не понимаю, что голосование "против", это так просто решить ??
Вероятно, инициализируется куча вещей, которые никогда не будут освобождены. Но GC.Collect
- это не ответ, вам нужно профилировать с помощью профилировщика памяти и найти утечки.
ToolStripMenuItem.Dispose()
не удаляет установленный вами образ. Он не может этого сделать: это может быть общий ресурс, который используют другие элементы управления. Но поскольку вы создаете этот образ с помощью Factory, скорее всего, вам также потребуется вызвать item.Image?.Dispose()
. Когда вы используете общий Bitmap, вам нужно избавиться только от него, когда класс, который его создал, удаляется (или, с Form, когда он закрывается).
привет, образ - это просто ресурс из приложения в Resources > images (build action: resource)
. item.Image?.Dispose()
ничем не отличается: S
Только что протестировал вашу настройку, заменив public static AppMain GetInstance() { if (appInstance == null) { appInstance = new AppMain(); } return appInstance; }
. В memoryLeakTest()
, как уже упоминалось, добавлено: item.Image?.Dispose(); item.Dispose();
(для item.Image
задан объект ресурса). Тогда цикл становится: var inst = AppMain.GetInstance(); for(...) { inst.memoryLeakTest(); } inst = null;
.
Конечно, есть начальное выделение памяти из-за создания объектов. После этого инструмент диагностики покажет использование памяти постоянный во время выполнения цикла и общий прирост памяти в 4,2 МБ, который не запускает сборку мусора, так как это не имеет значения (он также может быть собран позже или никогда, учитывая малый объем используемой памяти). GC.Collect()
, конечно, вообще не действует, так как все расположено идеально, как и ожидалось.
Спасибо за ваш отзыв, да, это в пределах ожидаемых результатов, хорошо, я сделаю несколько дополнительных тестов, потому что это не то, что я получаю, и я сообщу
привет, я создал этот образец программы (это просто Program.cs) paste.in/7J9mtx. Это не совсем то же самое, но почти. Вы можете запустить его? Из моих тестов Если удалить asyncCall (); или второй while () У меня нет утечки памяти, знаете почему? можете ли вы воспроизвести те же результаты?
Вы уверены, что это утечка? То, что сборщик мусора еще не собрал его, не означает, что он просочился. Все неправильно думают о сборке мусора