My .NET Assembly Is Not Unloading Until I Shut Down TestStand

Updated Apr 18, 2023

Reported In

Software

  • TestStand

Operating System

  • Windows

Programming Language

  • C# .NET
  • Visual Basic .NET

Issue Details

I am calling my .NET assembly from TestStand and storing the object reference in a FileGlobal. I notice that the assembly is not unloaded from memory when I would expect it to be. In most cases I can unload the assembly manually by selecting File>>Unload All Modules, but in some cases the only way I can unload the assembly from memory is by shutting down TestStand. Why does my .NET assembly remain in memory?

Solution

  1. Set the FileGlobal object reference(s) equal to Nothing at whatever point(s) in your tests that you want the assembly to be unloaded.
    • For TestStand 3.1 and earlier
      • Make sure to set the FileGlobal variable to Nothing before completing each execution and closing the Execution window, or it will not be released until the next garbage collection occurs. 
      • To explicitly trigger garbage collection, call GC.Collect in a code module.
    • For TestStand 3.5 and later
      • The frequency of periodic garbage collection can be configured by modifying the property Engine.DotNetGarbageCollectionInterval
  2. Unload all steps that call into the .NET assembly
    • For each applicable step:
      1. Navigate to Step Settings>>Properties>>Run Options
      2. Ensure the Load Option is not set to be loaded at any points where you want the assembly to be unloaded
        • Load dynamically will have the step loaded for the minimum amount of time, but it could slow test time
        • Refer to Properties Tab for more information
      3. Ensure the Load Option is not set to be loaded at any points where you want the assembly to be unloaded
        • Unload after step executes will have the step loaded for the minimum amount of time, but it could slow test time
        • Refer to Properties Tab for more information
    • You can also manually unload all the modules in TestStand through File>>Unload All Modules

Additional Information

TestStand cannot unload a .NET assembly from memory until TestStand unloads all steps that call into the .NET assembly and you release all objects created from the assembly. During execution of a sequence, TestStand maintains a run-time copy of FileGlobals and this copy is referenced by an Execution object. An Execution object that completes does not release references to FileGlobals, because the execution might still need them if it is restarted. An Execution object releases its references to FileGlobals when the Execution object is destroyed, typically when you close the Execution window.

If you store a .NET object reference in a FileGlobal variable, TestStand does not release the reference to the object until the variable is set equal to Nothing (see Special Constant Values for more details) or until TestStand releases all references to the parent FileGlobals container. Therefore, if you do not set the variable holding the reference equal to Nothing, .NET cannot destroy the object and TestStand cannot unload the assembly from memory until after you close the Execution window. TestStand effectively ignores step and sequence file unload options and any unload all module operations.

For example in TestStand 3.1, if you store a .NET object reference in a FileGlobal variable and pass a SequenceContext object that refers to the FileGlobals to a method of a .NET object, .NET releases its reference to the parameter after returning from the method and places the parameter reference in garbage collection. The undestroyed parameter maintains its reference to FileGlobals, so FileGlobals are not released until the next garbage collection occurs. The only workaround is to explicitly set the FileGlobal variable to Nothing, or call GC.Collect in a code module before completing the execution and closing the Execution window.

In TestStand 3.5 and later, the .NET adapter will periodically request .NET to garbage collect released objects to finalize their destruction and remove them from memory. This interval is set to a default of 3 seconds, but you can configure it manually by modifying the property Engine.DotNetGarbageCollectionInterval.