0

I'm currently working on an application that fetches some information from a database using a BW. It uses a stores number, referred to as IDP and searches the correct database. It works perfectly for my needs. However, each time it runs it's adding anywhere between 10-300 KBs to RAM, it is not releasing this memory once it completes. Since this code can be ran hundreds of times a day by numerous different people on a virtual machine with limited resources I really need it to release any memory it uses. Can anyone see where I'm going wrong?

Note: I'm self-taught and I'm doing this as more of a hobby that helps me out at work and not actually employed to do this, as I'm sure some of you will be happy to know once seeing my newbie code.

 Public Sub KickoffStoreBrief() 'Called when txtIDP (text box) text changes
        Dim args As BW_GetStoreBriefVariables = New BW_GetStoreBriefVariables()


        args.Current_IDP = txtIDP.Text.Trim

        If BW_GetStoreBrief.IsBusy Then
            MsgBox("Worker busy!")
        Else
            BW_GetStoreBrief.RunWorkerAsync(args)
        End If

 End Sub

Private Sub BW_GetStoreBrief_DoWork(sender As Object, e As DoWorkEventArgs) Handles BW_GetStoreBrief.DoWork

        Dim args As BW_GetStoreBriefVariables = DirectCast(e.Argument, BW_GetStoreBriefVariables) 'Convert the generic Object back into a MyParameters object

        Using DatabaseConnection As New SqlConnection(args.ConnectionString)
            Dim command As New SqlCommand(SQL CODE IS HERE, DatabaseConnection)
            command.CommandTimeout = 20

            'Attempt to open the connection
            command.Connection.Open()

            Dim reader As SqlDataReader = command.ExecuteReader()
            Dim dt As New DataTable()
            dt.Load(reader)
            reader = Nothing

            'Check if returned anything
            If dt.Rows.Item(0).Item(0) = Nothing Or dt.Rows.Item(0).Item(0).ToString = "False" Or dt.Rows.Item(0).Item(0).ToString = "" Then
                'Branch not found.
                GoTo Ender
            End If

            'Prefix 0's infront of the IDP as required
            Dim CompleteIDPNumber As String = ""
            If dt.Rows.Item(0).Item(0).ToString.Length < 4 Then
                If dt.Rows.Item(0).Item(0).ToString.Length = 2 Then
                    CompleteIDPNumber = "00" & dt.Rows.Item(0).Item(0).ToString
                ElseIf dt.Rows.Item(0).Item(0).ToString.Length = 3 Then
                    CompleteIDPNumber = "0" & dt.Rows.Item(0).Item(0).ToString
                Else
                    CompleteIDPNumber = dt.Rows.Item(0).Item(0).ToString
                End If
            Else
                CompleteIDPNumber = dt.Rows.Item(0).Item(0).ToString
            End If

            'Populate strings

            Dim StoreName As String = CompleteIDPNumber & " - " & dt.Rows.Item(0).Item(1).ToString.Trim
            Dim UISupports As Integer = 20 'This is the amount of characters that will fit in label space


            If StoreName.Length > UISupports Then
                StoreName = StoreName.Substring(0, UISupports).ToString.Trim & "..." & " (" & dt.Rows.Item(0).Item(3).ToString.Trim & ")"
            Else
                StoreName = StoreName & " (" & dt.Rows.Item(0).Item(3).ToString.Trim & ")"
            End If

            args.Brief_StoreName = StoreName

            StoreName = Nothing 'We no longer need this, release it from memory
            UISupports = Nothing 'We no longer need this, release it from memory
            CompleteIDPNumber = Nothing 'We no longer need this, release it from memory

            If dt.Rows.Item(0).Item(2) = 0 Or dt.Rows.Item(0).Item(2).ToString.Trim = "0" Then
                args.Brief_POSNumber = "IS"
            Else
                args.Brief_POSNumber = dt.Rows.Item(0).Item(2).ToString.Trim
            End If

            args.Brief_Category = dt.Rows.Item(0).Item(3).ToString 'CAT
            args.Brief_STCamera = dt.Rows.Item(0).Item(4).ToString 'Counter
            args.Brief_Franch = dt.Rows.Item(0).Item(5).ToString 

Ender:
            e.Result = args
            'Close connection
            dt.Dispose()
            command.Connection.Close()
            command.Dispose()
        End Using
    End Sub

    Private Sub BW_GetStoreBrief_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BW_GetStoreBrief.RunWorkerCompleted

        If e.Error IsNot Nothing Then
            ListBox1.Items.Insert(0, Now.ToString("HH:mm:ss") & " | Error | Unable to connect to store DB.")
            lblNotFound.Text = "Unable to connect to database."
            Panel_NotFound.Visible = True
            Panel_NotFound.BringToFront()
            ErrorLogger.LogError(System.Reflection.MethodBase.GetCurrentMethod().Name, e.Error.Message, 0)
        ElseIf e.Cancelled Then
        Else
            ' Access variables through args
            Dim args As BW_GetStoreBriefVariables = DirectCast(e.Result, BW_GetStoreBriefVariables) 'Convert the generic Object back into a MyParameters object

            If args.Brief_StoreName = "" Then
                ListBox1.Items.Insert(0, Now.ToString("hh:mm:ss") & " | Notice | IDP " & args.Current_IDP & " not found in database.")

                'show warning panel
                lblNotFound.Text = "Store not found in database."
                Panel_NotFound.Visible = True
                Panel_NotFound.BringToFront()
                GoTo Ender
            Else
                'Store found update UI

                lblBranchInfo_StoreName.Text = args.Brief_StoreName
                lblBranchInfo_POSNumber.Text = args.Brief_POSNumber
                lblBranchInfo_CameraType.Text = args.Brief_STCamera

                Panel_NotFound.Visible = False
                Panel_NotFound.SendToBack()
            End If

            args = Nothing

        End If

Ender:
        btnStoreDetails.Enabled = True
    End Sub

As you can see i've tried to make sure I'm not leaving anything behind, but the memory keeps jumping up and doesn't go down. Overall we're talking about 35MBs being used when this codes been ran only a few times and nothing else is happening with the program/form. Because this is on a remote virtual machine the program can be open for days without being closed, and with the memory usage increasing each time it will become a very big issue. Any help would be appreciated.

user3516240
  • 365
  • 1
  • 6
  • 25
  • 2
    `Dispose` your `SqlDataReader`, get rid of the `Goto` and use `try/catch/finally` to manage object lifetime... consider using `using` statements for `reader` and `dt`, and forget about assigning `Nothing` to variables - this doesn't dispose unmanaged objects. If an object implements `IDisposable` then you must `Dispose` it when you are finished with it. – J... Apr 12 '17 at 00:31
  • 1
    35 MB of memory in total is not a very large number... Keep in mind that applications tend to reserve some "released" memory so that they can use that in the future. This is what's called a process's [_**Private Bytes**_](http://stackoverflow.com/a/1986486/3740093), and is what Windows's Task Manager shows you by default. – Visual Vincent Apr 12 '17 at 00:34
  • 1
    To test if you actually have memory leaks, after following J...'s suggestions, you should run the code many times in a short period of time, stop, and see if the memory gets too high (i.e. 100+ MB) without dropping. – Visual Vincent Apr 12 '17 at 00:39
  • Thank you for your replies, I agree that 35 MB isn't a large number at all, but given how the program will be doing this a lot every day without closing I at least wanted to make sure I was doing the best I could. Thank you very much for your replies, they've helped me already and I'll work on your suggestions -J, in addition to reading up on Vincents link and testing for leaks as suggested. Thanks! :) – user3516240 Apr 12 '17 at 00:42
  • @user3516240 You can also look into the use of the **Garbage collector** and also don't forget to clear **Connection Pool** of the SQL as this will free any files opened for stream. – Mederic Apr 12 '17 at 06:52
  • Mmhhh I've done everything suggested, if I force garbage collection by doing GC.Collect() it keeps my memory usage down to 2 MBs.... Though from what I've read it's considered a bad idea to do this? – user3516240 Apr 12 '17 at 15:54
  • @user3516240 Yes, using `GC.Collect` is almost always a bad idea. If you don't know exactly why *you* are smarter than the runtime, and can explain your actions in terms of the framework, use case, and operating system mechanics, then `GC.Collect` is just a hacky crutch for bad code. Don't use it. – J... Apr 13 '17 at 08:20

0 Answers0