为啥这会导致内存泄漏?

Posted

技术标签:

【中文标题】为啥这会导致内存泄漏?【英文标题】:Why Is This Causing a Memory Leak?为什么这会导致内存泄漏? 【发布时间】:2009-05-21 17:45:28 【问题描述】:

这很有趣。我们花了最后一天尝试使用以下(遗留)代码修补问题,该代码继续增加其进程大小。这是在 Visual Studio 2003 中完成的。

我们有一个显示图像(来自 MemoryStream)、一些文本和一个按钮的表单。没有什么花哨。看起来像这样:

  Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
    MyBase.OnLoad(e)
    Try
      m_lblWarning.Visible = False

      m_grpTitle.Text = m_StationInterface.ProcessToolTitle
      m_lblMessage.Text = m_StationInterface.ProcessToolMessage

      Dim objImage As MemoryStream
      Dim objwebClient As WebClient
      Dim sURL As String = Trim(m_StationInterface.ProcessToolPicLocation)

      objwebClient = New WebClient

      objImage = New MemoryStream(objwebClient.DownloadData(sURL))
      m_imgLiftingEye.Image = Image.FromStream(objImage)

      m_txtAcknowledge.Focus()
    Catch ex As Exception
      '*** This handles a picture that cannot be found without erroring'
      m_lblWarning.Visible = True
    End Try
  End Sub

此表单经常关闭和打开。每次重新打开时,进程内存使用量大约增加 5mb。当表单关闭时,它不会回退到以前的用法。资源仍然分配给未引用的表单。表单呈现如下:

      m_CJ5Form_PTOperatorAcknowlegement = New CJ5Form_PTOperatorAcknowlegement
      m_CJ5Form_PTOperatorAcknowlegement.stationInterface = m_StationInterface
      m_CJ5Form_PTOperatorAcknowlegement.Dock = DockStyle.Fill
      Me.Text = " Acknowledge Quality Alert"

      '*** Set the size of the form'
      Me.Location = New Point(30, 30)
      Me.Size = New Size(800, 700)

      Me.Controls.Add(m_CJ5Form_PTOperatorAcknowlegement)

该控件稍后在关闭后从表单中移除:

Me.Controls.Clear()

现在。我们已经尝试了很多东西。我们发现 Disposing 什么都不做,事实上,IDisposable 接口实际上并没有触及内存。如果我们不每次都创建一个新的 CJ5Form_PTOperatorAcknowledgement 表单,则进程大小不会增长。但是将新图像加载到该表单中仍然会导致进程大小不断增长。

任何建议将不胜感激。

【问题讨论】:

【参考方案1】:

您必须处置您的 WebClient 对象和任何其他可能不再需要的托管非托管资源。


objImage = New MemoryStream(objwebClient.DownloadData(sURL))
objwebClient.Dispose() ' add call to dispose

一种更好的编码方式是使用“using”语句:


using objwebClient as WebClient = New WebClient      
objImage = New MemoryStream(objwebClient.DownloadData(sURL))      
end using

如需更多信息,请在 google 和 *** 上搜索“Dispose”和“IDisposable”模式实现。

最后一个提示:尽可能不要使用内存流。除非您需要将其保存在 RAM 中,否则直接从文件加载图像。

编辑

如果我正确理解您的代码,也许这样的事情会起作用:


Dim objImage As MemoryStream      
Dim objwebClient As WebClient      
Dim sURL As String = Trim(m_StationInterface.ProcessToolPicLocation)      
using objwebClient as WebClient = New WebClient      
  using objImage as MemoryStream = New MemoryStream(objwebClient.DownloadData(sURL))      
    m_imgLiftingEye.Image = Image.FromStream(objImage)
  end using
end using

【讨论】:

直到 Visual Studio 2005 才添加“使用”语句。处理不是这里的问题。我们已经尝试了几乎所有的对象处理变体,但都没有成功。我们还尝试存储文件的本地副本,而不是使用内存流。 你在使用 COM 对象吗?您可能需要在您的 COM 对象上调用 System.Runtime.InteropServices.Marshal.ReleaseComObject? 不涉及 COM 对象。到目前为止唯一有效的是重用相同的表单对象,而不是每次都实例化一个新的对象。如果我们不必每次都将新图像加载到表单上(这仍然会增加进程大小),那将是一个解决方案。就好像垃圾收集器无法识别不再引用旧对象一样。我们甚至可以在表单上省略任何功能性内容,并且流程规模仍在增长。 嗯,有很多变量,没有足够的上下文让我在这里继续猜测,但我会说你尝试过 Form.Dispose、Image.Dispose 等,等等......任何你的资源新的'你应该'处置'。此外,垃圾收集器会在自己的时间运行,您不会看到立即释放内存。如果您已覆盖“最终确定”,则可能会延长该内存的保留时间。 除了处理表单等...确保您手动删除使用“addhandler”添加的任何事件处理程序。【参考方案2】:

我不知道具体为什么会泄漏,但我可以建议您使用.NET Memory Profiler。如果你使用它来运行你的应用程序,它会让你很好地了解哪些对象没有被释放,并帮助你解决原因。它有免费试用版,但值得购买。

【讨论】:

以上是关于为啥这会导致内存泄漏?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用“新”会导致内存泄漏?

为啥 Netty ByteBuf.readBytes 会导致内存泄漏?

为啥这段代码不会导致内存泄漏? [复制]

为啥并行读取数组会导致内存泄漏?

为啥“[[UIDevice currentDevice] identifierForVendor]”会导致内存泄漏?

为啥这个 INotifyCollectionChanged 会导致内存泄漏?