【问题标题】:Datasets and Memory leaks数据集和内存泄漏
【发布时间】:2013-04-01 21:00:48
【问题描述】:

我正在尝试找出使用 DataSet/DataTable 并在之后正确清理的最佳方法。

我有点疑惑是什么导致内存被释放。我用一个测试应用程序测试了我的理论,我在一个循环中多次填充相同的DataTable,并在 3 次强制 GC 收集后查看 Windows 的任务管理器的内存占用情况。

我发现的是:

  1. 如果我没有调用ClearDispose,或者将DataTable变量设置为Nothing,任务管理器中最终的内存消耗约为30k。

  2. 如果我只是在循环内将变量设置为Nothing,最终的内存大约为15k。
    问题:为什么将变量设置为Nothing 会有所不同?

  3. 如果我只在循环内调用Dispose方法,最终的内存大约是19k。

  4. 如果我只在循环内调用Clear,最终的内存大约是 16.5k。事实上,即使在GC.Collect之后它也没有改变。

如果有人能分享在不再需要时使用和清理DataSets 的最佳方式,我将不胜感激。

示例代码如下所示。

Imports System.Data.SqlClient;
Public Class Form1

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        Test()

        GC.Collect()
        GC.Collect()
        GC.Collect() 'Throw in one more 
    End Sub


    Private Sub Test()
        Dim oDA As SqlDataAdapter = Nothing
        Dim oConn As SqlConnection = Nothing
        Dim oCommand As SqlCommand = Nothing
        Dim ods As DataSet = Nothing
        Dim oDt As DataTable = Nothing
        Try
            oConn = New SqlConnection("Server=Myserv;Database=myDB;UserId=myuserid;Password=mypassword;")
            oCommand = New SqlCommand("Select  * from Users", oConn)
            oConn.Open()
            ods = New DataSet

            oDA = New SqlDataAdapter(oCommand)

            For i As Integer = 0 To 50
                oDA.Fill(ods)
                oDt = ods.Tables(0)
                'oDt.Clear()
                'oDt.Dispose()
                oDt = Nothing
            Next
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        Finally
            ods.Clear()
            ods = Nothing
            oConn.Close()
            oDA = Nothing
        End Try
    End Sub
End Class

编辑:我正在寻找管理传递的DataSets 和/或DataTables 内存的最佳实践,其中创建方法不一定负责清理内存。另外,为什么在函数中将对象/变量设置为Nothing 与让它超出范围的效果不同。

【问题讨论】:

    标签: .net vb.net memory-management memory-leaks dataset


    【解决方案1】:

    “清理”数据的最佳方法是为任何实现 IDisposable1 的东西切换到 Using statement。一旦你实现了这个模式,你将拥有最合理的内存使用对于你设计的数据结构

    Using oConn As New SqlConnection("Server=Myserv;Database=myDB;UserId=myuserid;Password=mypassword;")
        oConn.Open()
        Using oCommand As New SqlCommand("Select  * from Users", oConn)
            ' other code as needed, wrap IDisposable in Using...EndUsing
        End Using
    End Using
    

    这并不能保证您的数据结构本身具有内存效率,只是它们只会在必要时保留资源。

    1。是的,你有时不能使用一个,但如果你不使用 using 语句是错误的前提下工作,你会更好。

    【讨论】:

    • 感谢您的回复。在可能的情况下,我将它们更改为使用这种“使用”模式。但是在很多情况下,数据集或数据表是被传递的,而创建者不一定要负责处理。我的问题更具体关于传递的数据集和数据表。抱歉,我最初的问题并不清楚。我会更新问题。
    【解决方案2】:

    “为什么在函数中将某事设置为空与只是让它超出范围执行不同?”

    你已经访问了这个东西,所以在此之前它不符合 GC 的条件。

    GC 发生的时间有点不确定:显然它会根据几个因素安排下一次调用(引用:http://channel9.msdn.com/Shows/Defrag-Tools/Defrag-Tools-33-CLR-GC-Part-1(视频))。

    【讨论】:

    • 但是在包含以下循环的函数返回后,我明确地调用了 GC.Collect 三次。对于 i As Integer = 0 To 50 oDA.Fill(ods) oDt = ods.Tables(0) 'oDt.Clear() 'oDt.Dispose() oDt = Nothing Next 。所以变量 oDt 在循环中被重用了 50 次。一旦将新对象分配给变量,那应该与将该变量设置为空一样好。但这里似乎不是这样,我不知道为什么。
    【解决方案3】:

    使用后必须释放的任何资源(包括 DataTable 或 Dataset)的最佳使用方式是尽可能使用关键字 Using。如果无法使用 Using 关键字,您应该执行与 Using 关键字等效的操作,例如: 尝试 { } 捕捉(例外前) { } 最后 { 对象.dispose() }

    根据 msdn,任何实现 IDisposable 的对象都应该使用 dispose 方法释放资源。因为 Dataset 和 DataTable 继承自实现 IDisposable 的 MarshalByValueComponent,所以最好调用 dispose 方法。如果有 是任何不实现 IDisposable 方法的资源,那么您可以实现 IDisposable 接口并覆盖 dispose 方法并在 dispose 方法中进行资源清理。通过将 DataSet 或 DataTable 设置为 null,您只需删除 最终将由垃圾收集器收集的参考。但是使用 dispose 方法是更好的方法。你没有 显式调用 GC.Collect 方法,因为垃圾收集器通常在后台运行,并且每当内存 低于某个阈值(由编译器确定),它将释放这些资源使用的内存。欲了解更多信息请 参考垃圾回收(http://msdn.microsoft.com/en-us/library/0xy59wtx(v=vs.110).aspx)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-28
      • 1970-01-01
      • 2019-12-18
      • 2011-09-19
      • 2014-02-27
      相关资源
      最近更新 更多