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
An instance of an array of
An instance of a generic type that has
Tas one of its type arguments. This includes generic collections of
T, even if that collection is empty.
You must release all objects that represent parts of the assembly. The ModuleBuilder that defines
Tkeeps 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 of
A static reference to
Tby another dynamically defined type
T1that is still reachable by executing code. For example,
T1might derive from
Tmight be the type of a parameter in a method of
A ByRef to a static field that belongs to
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 for
Tcan be obtained from an array type whose element type is
T, or from a generic type that has
Tas a type argument.
Mon the call stack of any thread, where
Mis a method of
Tor a module-level method that is defined in the assembly.
A delegate to a static method that is 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.
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 is 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 is used is an implementation detail and subject to change.
Restrictions on collectible assemblies
The following restrictions apply to collectible assemblies:
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.
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.
Methods that have the DllImportAttribute attribute will not compile when they are 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 marshaled to unmanaged code. However, you can call into native code by using an entry point that is declared in a non-collectible assembly.
Objects (in particular, delegates) that are defined in collectible assemblies cannot be marshaled. This is a restriction on all transient emitted types.
Reflection emit is the only mechanism that is supported for loading collectible assemblies. Assemblies that are loaded by using any other form of assembly loading cannot be unloaded.
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 variables are not supported.