【发布时间】: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 次尝试发送消息时出现错误。特别是在尝试填充此数组时。
是否有某种方法可以使我的对象不会超出范围并被垃圾收集?如果是这样,我将如何强制垃圾收集或找出将其保持在范围内的引用。
我希望这个伪代码可以清楚地说明我要描述的内容。本质上,我在运行该程序时多次调用determineDataWord 和ReadMessage 函数。在初始化该窗口表单数组时,我似乎在经过这么多次之后内存不足。我尝试在不初始化该数组的情况下运行程序,并且与我定义的 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