0

I have run into a strange situation where when I reassign an object variable to a new instance eventually over several iterations I appear to run out of memory. I get the error:

An unhandled exception of type "System.ComponentModel.Win32Exception" occured in "System.Windwos.Forms.dll"\ Additional Information: The handle is invalid

Console output from error :

I have tried looking that error message up online and some people seem to think that, "you are likely to be leaking handles, probably by not properly closing windows or disposing controls." However I am not opening a windows, i am just instantiating a new object instance of a windows form. Due to the nature of the code I am not able to share it, but I can give a general run down of what's causing the error.

Essentially in my main form I create a class called TransmitMessage inside this class it has a base class object named BaseSubaddress which is used to hold different types of subclasses called TransmitSubaddress (inherits directly from basesubaddress). Each time I want to process a message the class uses a function to determine which TransmitSubaddress to assign (1,2, or 3), each subaddress has its own specific routines. It will create a new instance of this object and assign it to the baseSubaddress variable.

When a new transmitsubaddress is created it fills an array of baseWordForms with subclasses that inherit directly from this base word form. When a new form is created it calls its InitializeComponent() function and then eventually calls the base class's InitializeComponenet() function.

My error is occurring when I try to send a message for the 43rd time. Specifically when trying to fill this array.

Is there some way that my objects aren't going out of scope and getting garbage collected? If so how would I go about forcing garbage collection or finding out what reference is keeping it in scope.

I hope this pseudocode can clearify what I am trying to describe. Essentially I am calling the determineDataWord and ReadMessage function many times over while running this program. It seems like I am running out of memory after so many times specifically when initializing that array of windows forms. I have tried running the program without initializing that array and was able to get 2000+ successful runs compared to the 43 I get with them defined. This leads me to believe that something in these forms is keeping them from being deallocated by garbage collection. When the time comes to actually invoke these forms I use the Using key word to try and avoid this issue.

Main form{

    TransmitMessage Tmsg;

    private void determineDataWord(){
       //logic for determining words
        tmsg = new transmitMessage(//stuff about transmitmessage);
    }

    private void readMessage(){
       //logic for reading words
        tmsg.translateTMsage(//stuff about message)
    } 
}

class TransmitMessage{
    baseSubaddress Tsubaddress;

    private void translateTMsage(//logic for msg){
        //logic for translation
        assignSubaddr(//info about msg);
        Tsubaddress.Translate(//infor about msg);
    }    

    private void assignSubaddr(//info about msg){
        //logic for determining subaddress    
        Tsubaddress = new TransmitSubaddress(1,2,3...);
   }   
}

class TransmitSubaddress : baseSubaddress{
   this.datawords = new baseWordForms[x]{new wordForm1, new wordfrom2....};

   public void Translate(){
       //logic for translation
   }

}

Upon creating a try catch structure to catch the exception i get the error message,

"An unhandled exception of type "System.outOfMemoryException" occured in "System.Drawing.dll" "Additional information: Out of memory"

error stack at system.windows.forms.ContrainerControl.GetFontAutoScakeDimensions()
at system.windows.forms.ContrainerControl.get_CurrentAutoScaleDimensions()
at system.windows.forms.ContrainerControl.get_AutoScaleFactor()
at system.windows.forms.ContrainerControl.PerformAutoScale(Boolean includedBounds, Boolean excludeBounds)
at system.windows.forms.ContrainerControl.PerformNeededAutoScaleOnLayout()
at system.windows.forms.Form.OnLayout(LayoutEventArgs Ievent)
at system.windows.forms.control.PerformLayout(LayoutEventArgs args)
at system.windows.forms.control.System.Windows.Forms.Layout.IArrangedElement.PerformLayout(IArrangedElement affectedElement,String affectedProperty)
at system.windows.forms.ContainerControl.LayoutScalingNeeded()
at system.windows.forms.ContainerControl.set_AutoScaleMode(AutoscaleMode Value)
at "xxx.baseDataWordForm.InitilizaComponent()"
S Rich
  • 1
  • 1
  • You explicitly keep those objects in memory since you store them in a field in the main form, which never goes out of scope while the application runs. Other than that, the code doesn't demonstrate any problem. You didn't post any exception, call stack or log either, so it's hard to understand what you ask. OOMs are typically caused by inefficient handling of buffers, eg trying to add 100K items in a list one by one. A list stores data in a buffer, copying the data into a new, larger buffer when the original is full. That leads to a lot of memory waste – Panagiotis Kanavos Jun 25 '20 at 14:59
  • Simply specifying the `capacity` parameter to any constructor that accepts it can eliminate OOM's even if it's inaccurate. Adding 1024 items would reallocate the buffer 10 times. Even setting the capacity to `512` would result in only 2 reallocations – Panagiotis Kanavos Jun 25 '20 at 15:02
  • @Panagiotis Kanavos Thanks for the response. I'll post a call stack + log after lunch if you guys are still around. I do store those fields in a field in the main form, but wouldn't that memory get deallocated once I call the determineDataWord() function that creates a new instance of the TransmitMessage object. – S Rich Jun 25 '20 at 15:28
  • Since your pseudocode snippet is not showing us everything, is it possible that your instances of `TransmitMessage Tmsg` had event handlers attached to events that never leave scope? If those event handlers were never removed, then the instances would never be garbage collected when reassigning the field `Tmsg` in `determineDataWord()`. There could be other reasons why they aren't garbage collected, but this is a common one. – Sean Skelly Jun 25 '20 at 17:04
  • Added a try catch statement to get the stacktrace – S Rich Jun 25 '20 at 17:39
  • @SeanSkelly No event are explicitly contained in the object. However, when a listboxDoubleclick event occurs it calls the Tmsg.invokeWordForm() method. Once control returns it eventually gets to the readmessage() method. Other buttonClick events also interact with Tmsg in a similar manner. Surely this isn't what you meant by having those event handlers tied to the Tmsg – S Rich Jun 25 '20 at 17:47

1 Answers1

0

I found a solution. If anyone runs into this same issue you need to call the dispose() method on each one of these form objects before instantiating a new instance.

//strictly psudocode don't attempt to run
class TransmitMessage{
    baseSubaddress Tsubaddress;

    private void translateTmesage(//logic for msg){
        //logic for translation
        assignSubaddr(//info about msg);
        Tsubaddress.Translate(//infor about msg);
    }    

    private void assignSubaddr(//info about msg){
        //logic for determining subaddress   
        ClearWords(int numberOfWords) 
        Tsubaddress = new TransmitSubaddress(1,2,3...);
   }   
    private void ClearWords(int numberOfWords){
     if (Tsubaddress != null){
         for(int i = 0; i< numberOfWords; i++){
          Tsubaddress.dataWords[i].dispose;
         }
     }
    }
}
S Rich
  • 1
  • 1