ICorProfilerCallback::ClassUnloadStarted not called for a generic class instantiation, even though it was unloaded RRS feed

  • Question

  • Hi CLR forum team,

    My company is developing a CLR profiler which keeps track of loaded classes based on ClassIDs, by following the ClassLoadStarted, ClassLoadFinished and ClassUnloadStarted callbacks. In some scenarios, where modules are unloaded, we've noticed that certain generic classes get unloaded, without ClassUnloadStarted being called for them. This happens only after module unloading, and only for certain generic classes. Then, when we try to query about the ClassID using any API function, thinking its still loaded and valid, the CLR crashes (unsurprisingly, since the ClassID were invalidated).

    We can tell that the relevant class was indeed unloaded, by looking at its memory (ClassID address) in Visual Studio's memory view and seeing that it was released (as opposed to other, valid ClassIDs). Also, the owning module was itself unloaded.

    My question: is this possible (by design) for a generic class (or any other class for that matter), in certain scenarios, to be unloaded without ClassUnloadStarted being called? Or, could this be a CLR/profiling API bug?

    We've met this issue with several sites running on iisexpress, with .NET 4.7.2, and also with latest 4.8 preview (4.8.03745).

    After research, we were able to replicate this behaviour with a small example project, that triggers module unloading by manually unloading AppDomains. A simple generic class was enough for the issue to occur, but not any generic class did.
    Here is the example project:

    Below is the original scenario that we've faced:

    - The class in question is IComparable<ClassFromModuleFoo>.
    - The load/unload callbacks flow, based on debug prints added in the Load/Unload callbacks:
       1. IComparable<ClassFromModuleFoo> (mscorlib) is loaded.
       2. The class ClassFromModuleFoo is loaded into assembly #1.
       3. Module Foo finishes loading into assembly #1.
       4. Then, module Foo is loaded again into a different assembly, #2.
       5. IComparable<ClassFromModuleFoo> and ClassFromModuleFoo are loaded again, this time in assembly #2. Now there are two instances of each class: one in Foo loaded in assembly #1, and one in Foo loaded in assembly #2.
       6. Module Foo begins to unload from assembly #1.
       7. ClassUnloadStarted callback is called for ClassFromModuleFoo in assembly #1.
       8. Module Foo finished to unload from assembly #1.
       9. ClassUnloadStarted is *not* called for IComparable'1<ClassFromModuleFoo> of assembly #1 anytime later, even though its module unloaded and its ClassID points to now thrashed memory.

    Note, our ClassUnloadStarted callback implementation is quite simple, namely unregistering the unloaded ClassID from the relevant data structures.

    Please let me know if there is any additional information that may be helpful.

    Thank you!

    • Edited by Valiano Monday, March 11, 2019 7:06 AM
    Saturday, March 9, 2019 1:59 PM

All replies

  • Hi Valiano,

    To further help you about this issue, I am trying to invoke someone experienced to help look into this thread, this may take some time and as soon as we get any result, we will post back.

    Best Regards,


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact

    Tuesday, March 12, 2019 7:34 AM
  • Hi Wendy,

    Thanks for your reply. Could you please advice if there's any update on this issue?

    Since reporting, we were able to further validate this behaviour, and also noticed the opposite: generic class instantiations (ClassIDs) that we get ClassUnload notifications for, but for which we don't get ClassLoad notifications. It would be really helpful and interesting to understand this CLR behaviour.

    Kind regards
    Friday, April 5, 2019 7:50 AM