Почему делает вызов AppDomain. Разгрузитесь не приводит к сборке "мусора"?

Когда я выполняю AppDomain. Разгрузитесь (myDomain), я ожидаю, что это также сделает полную сборку "мусора".

По словам Jeffrey Richter в "CLR через C#" он говорит это во время AppDomain. Разгрузитесь:

CLR вынуждает сборку "мусора" произойти, исправляя память, используемую любыми объектами, которые были созданы теперь разгруженным AppDomain. Завершить методы для этих объектов называют, давая объектам шанс вымыться правильно.

По словам "Steven Pratschner" в "настройке общеязыковой среды выполнения платформы.NET":

После того, как все финализаторы работали, и больше потоков не выполняется в домене, CLR готов разгрузить все структуры данных в оперативной памяти, используемые во внутренней реализации. Прежде чем это происходит, однако, объекты, которые находились в домене, должны быть собраны. После того, как следующая сборка "мусора" происходит, структуры данных домена приложения разгружены от адресного пространства процесса, и домен считают разгруженным.

Я неправильно истолковываю их слова? Я сделал следующее решение воспроизвести неожиданное поведение (в .net 2.0 sp2):

Проект библиотеки классов под названием "Интерфейсы", содержащие этот интерфейс:

   public interface IXmlClass
    {
        void AllocateMemory(int size);

        void Collect();
    }

Проект библиотеки классов под названием "ClassLibrary1", какие ссылки "Интерфейсы" и содержат этот класс:

public class XmlClass : MarshalByRefObject, IXmlClass
{

    private byte[] b;

    public void AllocateMemory(int size)
    {
        this.b = new byte[size];
    }

    public void Collect()
    {
        Console.WriteLine("Call explicit GC.Collect() in " + AppDomain.CurrentDomain.FriendlyName + " Collect() method");
        GC.Collect();
        Console.WriteLine("Number of collections: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
    }

    ~XmlClass()
    {
        Console.WriteLine("Finalizing in AppDomain {0}", AppDomain.CurrentDomain.FriendlyName);
    }
}

Проект консольного приложения, какой ссылочный проект "Интерфейсов" и делает следующую логику:

static void Main(string[] args)
{
    AssemblyName an = AssemblyName.GetAssemblyName("ClassLibrary1.dll");
    AppDomain appDomain2 = AppDomain.CreateDomain("MyDomain", null, AppDomain.CurrentDomain.SetupInformation);
    IXmlClass c1 = (IXmlClass)appDomain2.CreateInstanceAndUnwrap(an.FullName, "ClassLibrary1.XmlClass");
    Console.WriteLine("Loaded Domain {0}", appDomain2.FriendlyName);
    int tenmb = 1024 * 10000;
    c1.AllocateMemory(tenmb);
    Console.WriteLine("Number of collections: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
    c1.Collect();
    Console.WriteLine("Unloaded Domain{0}", appDomain2.FriendlyName);
    AppDomain.Unload(appDomain2);
    Console.WriteLine("Number of collections after unloading appdomain:  Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
    Console.WriteLine("Perform explicit GC.Collect() in Default Domain");
    GC.Collect();
    Console.WriteLine("Number of collections: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
    Console.ReadKey();
}

Вывод при выполнении консольного приложения:

Loaded Domain MyDomain
Number of collections: Gen0:0 Gen1:0 Gen2:0
Call explicit GC.Collect() in MyDomain Collect() method
Number of collections: Gen0:1 Gen1:1 Gen2:1
Unloaded Domain MyDomain
Finalizing in AppDomain MyDomain
Number of collections after unloading appdomain:  Gen0:1 Gen1:1 Gen2:1
Perform explicit GC.Collect() in Default Domain
Number of collections: Gen0:2 Gen1:2 Gen2:2

Вещи заметить:

  1. Сборка "мусора" сделана для каждого процесса (просто напоминание)

  2. Объектам в appdomain, который разгружен, назвали финализатор, но сборка "мусора" не сделана. Объект на 10 мегабайтов, созданный AllocateMemory (), будет только собран после выполнения явного GC.Collect () в вышеупомянутом примере (или если сборщик "мусора" будет в некоторое время спустя.

Другие примечания: действительно не имеет значения, если XmlClass finalizable или нет. То же поведение происходит в вышеупомянутом примере.

Вопросы:

  1. Почему делает вызов AppDomain. Разгрузитесь не приводит к сборке "мусора"? Там какой-либо путь состоит в том, чтобы сделать тот результат вызова в сборке "мусора"?

  2. В AllocateMemory () я планирую загрузить недолгие большие xml документы (меньше или равный 16 МБ), который войдет в "кучу" LargeObject и будет объектами поколения 2. Там какому-либо пути состоял в том, чтобы собрать память, не обращаясь к явному GC.Collect () или другой вид явного программного управления сборщика "мусора"?

20
задан svick 29 July 2012 в 20:05
поделиться

2 ответа

Дополнительные заметки:

После некоторого обмена письмами с Джеффри Рихтером, который был достаточно любезен, чтобы взглянуть на вопрос:

OK, я прочитал ваше сообщение.
Во-первых, массив не будет GC'иться, пока не будет GC'иться объект XMLClass, а для сбора этого объекта требуется ДВА GC, потому что он содержит метод Finalize.
Во-вторых, выгрузка appdomain по крайней мере выполняет фазу маркировки GC, поскольку это единственный способ определить, какие объекты недостижимы, чтобы можно было вызвать их методы Finalize.
Однако компактная часть GC может выполняться или не выполняться при выгрузке GC. Вызов GC.CollectionCount явно не говорит всей истории. Он не показывает, что фаза разметки GC действительно произошла.
Кроме того, возможно, что AppDomain.Unload запускает GC с помощью какого-то внутреннего кода, который не приводит к увеличению переменных счета коллекции. Мы уже точно знаем, что выполняется фаза маркировки, а счетчик коллекции не отражает этого.

Лучшим тестом было бы посмотреть на некоторые адреса объектов в отладчике и увидеть, происходит ли уплотнение на самом деле. Если да (а я подозреваю, что да), то счетчик коллекций просто не обновляется правильно.

Если вы хотите разместить это на веб-сайте в качестве моего ответа, вы можете.

После того, как я последовал его совету и изучил SOS (также удалил финализатор), выяснилось следующее:

Перед AppDomain.Unload:

!EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0180b1f0
generation 1 starts at 0x017d100c
generation 2 starts at 0x017d1000
ephemeral segment allocation context: none
 segment    begin allocated     size
017d0000 017d1000  01811ff4 0x00040ff4(266228)
Large object heap starts at 0x027d1000
 segment    begin allocated     size
027d0000 027d1000  02f75470 0x007a4470(8012912)
Total Size  0x7e5464(8279140)
------------------------------
GC Heap Size  0x7e5464(8279140)

После AppDomain.Unload (те же адреса, уплотнение кучи не производилось)

!EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0180b1f0
generation 1 starts at 0x017d100c
generation 2 starts at 0x017d1000
ephemeral segment allocation context: none
 segment    begin allocated     size
017d0000 017d1000  01811ff4 0x00040ff4(266228)
Large object heap starts at 0x027d1000
 segment    begin allocated     size
027d0000 027d1000  02f75470 0x007a4470(8012912)
Total Size  0x7e5464(8279140)
------------------------------
GC Heap Size  0x7e5464(8279140)

После GC.Collect() адреса отличаются, что указывает на уплотнение кучи.

!EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x01811234
generation 1 starts at 0x0180b1f0
generation 2 starts at 0x017d1000
ephemeral segment allocation context: none
 segment    begin allocated     size
017d0000 017d1000  01811ff4 0x00040ff4(266228)
Large object heap starts at 0x027d1000
 segment    begin allocated     size
027d0000 027d1000  027d3240 0x00002240(8768)
Total Size   0x43234(274996)
------------------------------
GC Heap Size   0x43234(274996)

После большего количества sos я пришел к выводу, что это наверняка сделано специально, и что уплотнение кучи не обязательно выполняется. Единственное, в чем вы можете быть уверены во время выгрузки AppDomain, это в том, что объекты будут помечены как недоступные и будут собраны во время следующей сборки мусора (которая, как я уже сказал, не выполняется именно в момент выгрузки домена приложения, если только нет совпадений).

EDIT: Я также спросил Маони Стивенс, которая работает непосредственно в команде GC. Вы можете прочитать ее ответ где-то в комментариях здесь. Она подтверждает, что это сделано специально. Дело закрыто :)

18
ответ дан 30 November 2019 в 01:02
поделиться
  1. Вероятно, намеренно, но я не понимаю, зачем вам такое поведение (явное GC.Collect). Пока финализаторы вызываются, объекты удаляются из очереди финализаторов и при необходимости готовы к сборке мусора (при необходимости включается поток gc).

  2. Вы, вероятно, можете использовать какое-нибудь неприятное неуправляемое выделение и какое-то тяжелое взаимодействие, или закодировать его на неуправляемом C ++, а затем использовать управляемую оболочку для доступа к нему через C #, но пока вы остаетесь в управляемом мире .Net, нет.

    Более разумно взглянуть на вашу архитектуру еще раз, вместо того, чтобы пытаться играть роль сборщика мусора.

5
ответ дан 30 November 2019 в 01:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: