Я прочитал эту статью и эту тоже.
Я пытался реализовать деструктор в простом коде.
class Program
{
static void Main(string[] args)
{
CreateSubscriber();
Console.Read();
}
static void CreateSubscriber()
{
Subscriber s = new Subscriber();
s.list.Clear();
}
}
public class Subscriber
{
public List<string> list = new List<string>();
public Subscriber()
{
for(long i = 0; i < 1000000; i++)
{
list.Add(i.ToString());
}
}
~Subscriber()
{
//this line is only performed on explicit GC.Collect()
Console.WriteLine("Destructor Called - Sub");
}
}
Когда код достиг строки Console.Read()
, экземпляр Subscriber
больше не находился в области видимости (я ожидал, что он будет иметь право на сборку мусора).Я оставил приведенный выше код в течение почти 2 часов в ожидании destructor
из Subscriber
.но это никогда не вызывало, ни память, занятая освобожденным кодом.
Я понял, в c # мы не можем назвать деструкторы программными, и он автоматически вызывается на Garbage collection
, поэтому я попытался вызвать GC.Collect()
явно.
Делая это, я мог видеть, что деструктор был вызван.Так что в моем коде выше сборка мусора не производилась!Но почему?
Это потому, что программа однопоточная и этот поток ожидает ввода данных пользователем на Console.Read()
?
Или у него есть что-то со списком строк?если да, то что это
Обновление (для будущих читателей)
, как Фабьян предложил в своем ответе
Скорее всего где-токогда создается новый объект и для него выделяется память, GC выполняет проверку всех ссылок и собирает первый объект.
и предлагает попробовать
CreateSubscriber();
Console.Read();
CreateSubscriber();
Console.Readkey();
Iобновил код, как показано ниже,
class Program
{
static void Main(string[] args)
{
CreateSubscriber(true);
Console.ReadLine();
CreateSubscriber(false);
Console.ReadLine();
}
static void CreateSubscriber(bool toDo)
{
Subscriber s = new Subscriber(toDo);
s.list.Clear();
}
}
public class Subscriber
{
public List<string> list = new List<string>();
public Subscriber(bool toDo)
{
Console.WriteLine("In Consutructor");
if (toDo)
{
for (long i = 0; i < 5000000; i++)
list.Add(i.ToString());
}
else
{
for (long i = 0; i < 2000000; i++)
list.Add(i.ToString());
}
Console.WriteLine("Out Consutructor");
}
~Subscriber()
{
Console.WriteLine("Destructor Called - Sub");
}
}
вывод:
и, как он ожидал, при создании второго экземпляра Subscriber
, я мог видеть сбор GC (вызывался финализатор).
Обратите внимание, что: в другом условии конструктора Subscriber, я добавляю меньше элементов в список, чем если условие - чтобы заметить, используется ли приложение в оперативной памяти приложения.соответственно уменьшилось, да оно тоже уменьшается.
там в другом состоянии, я мог бы оставить список строк пустым (поэтому использование памяти будет значительно уменьшено).Но при этом GC не собирается.Скорее всего, из-за причины, упомянутой М.Аруси в комментарии к вопросу.
в дополнение к тому, что сказано выше, сборщик мусора будет собирать данные только после того, как поколение заполнится (или из-за явного вызова)и только 1 созданный объект не будет вызывать его.Да, объект пригоден для завершения, но у GC нет причин его собирать.