Collectible assemblies for dynamic type generation
Collectible assemblies are dynamic assemblies that can be unloaded without unloading the application domain in which they were created. All managed and unmanaged memory used by a collectible assembly and the types it contains can be reclaimed. Information such as the assembly name is removed from internal tables.
To enable unloading, use the AssemblyBuilderAccess.RunAndCollect flag when you create a dynamic assembly. The assembly is transient (that is, it cannot be saved) and is subject to limitations described in the Restrictions on collectible assemblies section. The common language runtime (CLR) unloads a collectible assembly automatically when you release all objects associated with the assembly. In all other respects, collectible assemblies are created and used in the same way as other dynamic assemblies.
Lifetime of collectible assemblies
The lifetime of a collectible assembly is controlled by the existence of references to the types it contains and the objects that are created from those types. The common language runtime does not unload an assembly as long as one or more of the following exist (T
is any type that is defined in the assembly):
An instance of
T
.An instance of an array of
T
.An instance of a generic type that has
T
as one of its type arguments. This includes generic collections ofT
, even if that collection is empty.An instance of Type or TypeBuilder that represents
T
.Important
You must release all objects that represent parts of the assembly. The ModuleBuilder that defines
T
keeps a reference to the TypeBuilder, and the AssemblyBuilder object keeps a reference to the ModuleBuilder, so references to these objects must be released. Even the existence of a LocalBuilder or an ILGenerator used in the construction ofT
prevents unloading.A static reference to
T
by another dynamically defined typeT1
that is still reachable by executing code. For example,T1
might derive fromT
, orT
might be the type of a parameter in a method ofT1
.A
ByRef
to a static field that belongs toT
.A RuntimeTypeHandle, RuntimeFieldHandle, or RuntimeMethodHandle that refers to
T
or to a component ofT
.An instance of any reflection object that could be used indirectly or directly to access the Type object that represents
T
. For example, the Type object forT
can be obtained from an array type whose element type isT
, or from a generic type that hasT
as a type argument.A method
M
on the call stack of any thread, whereM
is a method ofT
or a module-level method that is defined in the assembly.A delegate to a static method that's defined in a module of the assembly.
If only one item from this list exists for only one type or one method in the assembly, the runtime cannot unload the assembly.
Note
The runtime does not actually unload the assembly until finalizers have run for all items in the list.
For purposes of tracking lifetime, a constructed generic type such as List<int>
(in C#) or List(Of Integer)
(in Visual Basic) that's created and used in the generation of a collectible assembly is considered to have been defined either in the assembly that contains the generic type definition or in an assembly that contains the definition of one of its type arguments. The exact assembly that's used is an implementation detail and subject to change.
Restrictions on collectible assemblies
The following restrictions apply to collectible assemblies:
Static references
Types in an ordinary dynamic assembly cannot have static references to types that are defined in a collectible assembly. For example, if you define an ordinary type that inherits from a type in a collectible assembly, a NotSupportedException exception is thrown. A type in a collectible assembly can have static references to a type in another collectible assembly, but this extends the lifetime of the referenced assembly to the lifetime of the referencing assembly.
The following restrictions apply to collectible assemblies in .NET Framework:
COM interop
No COM interfaces can be defined within a collectible assembly, and no instances of types within a collectible assembly can be converted into COM objects. A type in a collectible assembly cannot serve as a COM callable wrapper (CCW) or runtime callable wrapper (RCW). However, types in collectible assemblies can use objects that implement COM interfaces.
Platform invoke
Methods that have the DllImportAttribute attribute will not compile when they're declared in a collectible assembly. The OpCodes.Calli instruction cannot be used in the implementation of a type in a collectible assembly, and such types cannot be marshalled to unmanaged code. However, you can call into native code by using an entry point that's declared in a non-collectible assembly.
Marshaling
Objects (in particular, delegates) that are defined in collectible assemblies cannot be marshalled. This is a restriction on all transient emitted types.
Assembly loading
Reflection emit is the only mechanism that's supported for loading collectible assemblies. Assemblies that are loaded by using any other form of assembly loading cannot be unloaded.
Context-bound objects
Context-static variables are not supported. Types in a collectible assembly cannot extend ContextBoundObject. However, code in collectible assemblies can use context-bound objects that are defined elsewhere.
Thread-static data
Thread-static variables aren't supported.
The following restrictions apply to collectible assemblies in .NET Framework and .NET versions prior to .NET 9:
Static fields with
FixedAddressValueTypeAttribute
Static fields that are defined in collectible assemblies cannot have the FixedAddressValueTypeAttribute attribute applied.