【问题标题】:C# objects not going out of scope and crashing the systemC# 对象不会超出范围并导致系统崩溃
【发布时间】:2020-10-16 02:00:31
【问题描述】:

我遇到了一种奇怪的情况,当我将一个对象变量重新分配给一个新实例时,最终经过多次迭代,我似乎内存不足。我得到了错误:

“System.Windwos.Forms.dll”中出现“System.ComponentModel.Win32Exception”类型的未处理异常\ 附加信息:句柄无效

错误的控制台输出:

我曾尝试在网上查找该错误消息,但有些人似乎认为,“您可能正在泄漏句柄,可能是由于未正确关闭窗口或处置控件。”但是我没有打开一个窗口,我只是在实例化一个窗口窗体的新对象实例。由于代码的性质,我无法分享它,但我可以大致了解导致错误的原因。

基本上在我的主窗体中,我在这个类中创建了一个名为TransmitMessage 的类,它有一个名为BaseSubaddress 的基类对象,用于保存不同类型的名为TransmitSubaddress 的子类(直接从basesubaddress 继承)。每次我想处理一条消息时,该类使用一个函数来确定分配哪个TransmitSubaddress(1,2 或 3),每个子地址都有自己特定的例程。它将创建该对象的一个​​新实例并将其分配给 baseSubaddress 变量。

创建new transmitsubaddress 时,它会使用直接继承自此基本单词形式的子类填充baseWordForms 数组。创建新表单时,它会调用其InitializeComponent() 函数,然后最终调用基类的InitializeComponenet() 函数。

我第 43 次尝试发送消息时出现错误。特别是在尝试填充此数组时。

是否有某种方法可以使我的对象不会超出范围并被垃圾收集?如果是这样,我将如何强制垃圾收集或找出将其保持在范围内的引用。

我希望这个伪代码可以清楚地说明我要描述的内容。本质上,我在运行该程序时多次调用determineDataWordReadMessage 函数。在初始化该窗口表单数组时,我似乎在经过这么多次之后内存不足。我尝试在不初始化该数组的情况下运行程序,并且与我定义的 43 次相比,能够成功运行 2000 多次。这让我相信这些形式的某些东西阻止了它们被垃圾回收释放。当需要实际调用这些表单时,我会使用 Using 关键字来尝试避免此问题。

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
   }

}

在创建一个 try catch 结构来捕获异常时,我会收到错误消息,

““System.Drawing.dll”中出现“System.outOfMemoryException”类型的未处理异常“附加信息:内存不足”

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()"

【问题讨论】:

  • 您明确地将这些对象保存在内存中,因为您将它们存储在主窗体的一个字段中,在应用程序运行时它永远不会超出范围。除此之外,代码没有显示任何问题。您也没有发布任何异常、调用堆栈或日志,因此很难理解您的要求。 OOM 通常是由缓冲区处理效率低下引起的,例如,试图在列表中逐一添加 100K 项。列表将数据存储在缓冲区中,当原始缓冲区已满时,将数据复制到新的更大的缓冲区中。这会导致大量内存浪费
  • 只需将capacity 参数指定给任何接受它的构造函数即可消除OOM,即使它不准确。添加 1024 个项目将重新分配缓冲区 10 次。即使将容量设置为 512 也只会导致 2 次重新分配
  • @Panagiotis Kanavos 感谢您的回复。如果你们还在的话,我会在午餐后发布一个调用堆栈+日志。我确实将这些字段存储在主表单的字段中,但是一旦我调用创建 TransmitMessage 对象的新实例的确定数据字()函数,该内存不会被释放。
  • 由于您的伪代码 sn-p 没有向我们展示所有内容,您的 TransmitMessage Tmsg 实例是否有可能将事件处理程序附加到永远不会离开范围的事件?如果这些事件处理程序从未被删除,那么在重新分配 determineDataWord() 中的字段 Tmsg 时,这些实例将永远不会被垃圾收集。可能还有其他原因导致它们没有被垃圾回收,但这是一个常见的原因。
  • 添加了 try catch 语句来获取堆栈跟踪

标签: c# winforms window-handles unmanagedresources


【解决方案1】:

我找到了解决方案。如果有人遇到同样的问题,您需要在实例化新实例之前对每个表单对象调用 dispose() 方法。

//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;
         }
     }
    }
}

【讨论】:

    猜你喜欢
    • 2015-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-25
    • 1970-01-01
    • 1970-01-01
    • 2020-12-29
    • 2019-07-12
    相关资源
    最近更新 更多