.NET использует сборщик мусора из поколения в поколение, и не имеет ничего подобного детерминированной распределения памяти (если не прибегать к небезопасным кода и структуры везде, конечно).
Наиболее важной частью здесь является то, что при каждом распределении время выполнения проверяет, сколько памяти было выделено с момента последней попытки сбора мусора, и если это проходит определенный порог, начинается сбор. Таким образом, он будет проходить через всю память (для коллекции поколения 2 - сбор низшего поколения собирается только через кучи более низкого поколения), обратите внимание на все объекты, которые не имеют к ним ссылок, и очищает их. Наконец, это будет compact куча - переместите все объекты вокруг, чтобы сделать смежное пространство в куче. Это очень важно, так как .NET не выделяет середину кучи [1] - это что-то вроде суффиксного стека, который позволяет «выскакивать» из середины.
Когда это будет сделано, все объекты, оставшиеся в живых, будут переведены в кучу следующего поколения (если они не находятся в максимальной генерации, две на момент написания этой статьи).
В этом разница между вариантом со списком и вариантом без него. При большем количестве распределений экземпляры старой формы были исправлены, как и следовало ожидать, но только тогда, когда в ней есть достаточно ресурсов. Существуют и другие скрытые затраты - возможно, первая инициализация потребовала загрузки некоторой библиотеки или некоторой общей инициализации. Вот почему вы всегда хотите использовать какую-то форму разминки перед выполнением каких-либо тестов. Кроме того, в любом случае память процесса не так важна - если вы хотите устранить проблемы с памятью, CLR Profiler или что-то подобное гораздо полезнее.
Вы можете заставить сборщик мусора выполнять свою работу, позвонив по телефону GC.Collect
, хотя это не рекомендуется. Вы не должны действительно нуждаться в этом, почти всегда. Просто привыкнуть к тому, чтобы не иметь идеального контроля над распределением памяти и освобождением - вы используете многопоточную, до-упреждающую многозадачную, виртуализованную в памяти систему, которая, возможно, распространена в наши дни.Точный контроль над памятью является иллюзией: D
Еще один важный момент - непонимание еще одной вещи о компиляторе и времени выполнения. Присвоение null
локальному не делает ничего - если вы работаете за пределами отладчика, местный будет иметь право на сбор, как только он больше не будет использоваться. Если вы работаете внутри отладчика, все локали сохраняются для всей области (для облегчения отладки, конечно). Кроме того, избегайте инициализации местных жителей, когда у вас нет разумного значения для их инициализации, - вы лишаете себя помощи компилятора при показе неожиданных кодовых файлов.
[1] Обратите внимание, что это относится только к основным кучам. Большая куча объектов действительно позволяет выделять посередине, и она не компактна. Начиная с .NET 4.5, есть возможность вручную принудительно создать коллекцию кучи на LOH.
Боковое примечание: 'list = null;' в лучшем случае избыточно. GC и JIT сотрудничают, чтобы понять время жизни переменных. Они уже знают, в конце метода, что переменная 'list' больше не является« живой »ссылкой, и эта переменная не может использоваться для сохранения объекта ссылки. –
Имейте в виду, что существует ограниченное количество ручек ресурсов, необходимых каждой форме. Это может осложнить ваши тесты. Вероятно, лучше избегать использования любой формы пользовательского интерфейса для тестов памяти. – Enigmativity