Когда я выполняю AppDomain.Unload (myDomain), я ожидаю, что он также выполнит полную сборку мусора.
По словам Джеффри Рихтера в «CLR via C #», он говорит, что во время AppDomain.Unload:
CLR вызывает сборку мусора, освобождая память, используемую любыми объектами
которые были созданы теперь незагруженным AppDomain. Завершить методы для
эти объекты называются, что дает им возможность правильно очиститься.
Согласно «Стивену Пратшнеру» в «Настройка общеязыковой среды выполнения .NET Framework»:
После того как все финализаторы запущены и в домене больше нет потоков, CLR готов выгрузить все структуры данных в памяти, используемые во внутренней реализации. Однако прежде чем это произойдет, объекты, находящиеся в домене, должны быть собраны. После следующей сборки мусора структуры данных домена приложения выгружаются из адресного пространства процесса, и домен считается выгруженным.
Я неправильно истолковал их слова?
Я сделал следующее решение для воспроизведения неожиданного поведения (в .net 2.0 sp2):
Проект библиотеки классов под названием «Интерфейсы», содержащий этот интерфейс:
public interface IXmlClass
{
void AllocateMemory(int size);
void Collect();
}
Проект библиотеки классов с именем "ClassLibrary1", который ссылается на "Interfaces" и содержит этот класс:
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
На что обратить внимание:
Сборка мусора выполняется за процесс (только переподготовка)
Объекты в домене приложения, которые выгружаются, вызывают финализатор, но сборка мусора не выполняется. 10-мегабайтный объект, созданный AllocateMemory (), будет собран только после выполнения явного GC.Collect () в приведенном выше примере (или если сборщик мусора будет через некоторое время позже.
Другие примечания: не имеет значения, является ли XmlClass финализируемым или нет. То же самое происходит в приведенном выше примере.
Вопросы:
Почему вызов AppDomain.Unload не приводит к сборке мусора? Есть ли способ сделать этот вызов результатом сборки мусора?
Внутри AllocateMemory () я планирую загрузить недолговечные большие XML-документы (меньше или равные 16 МБ), которые попадут в кучу LargeObject и будут объектами поколения 2. Есть ли способ собрать память, не прибегая к явному GC.Collect () или другому виду явного программного управления сборщиком мусора?