Memory creaping up...why?

J

Jerry

I have a simple subroutine (see below) which reads a table and populates the
items of a combobox. As the subroutine is executed, I'm watching the
taskmanager and the memory in use increases; however, once I get to the
Error_Exit: logic and start to close and dispose my memory variables, the
memory is not released and does not decrease in the task manager. Why is
that? Eventually, my program is giving me "Insufficient memory" errors.

****************************************************************************************

On Error GoTo Error_Handling

Dim cnn As New Odbc.OdbcConnection
Dim cmd As New Odbc.OdbcCommand
Dim rdr As Odbc.OdbcDataReader

cnn.ConnectionString = "dsn=" & accpac_companyid
cnn.Open()
cmd.Connection = cnn
cmd.CommandText = "select schedkey, scheddesc from cssktb order by schedkey"
rdr = cmd.ExecuteReader

Do While rdr.Read
combo.Items.Add(rdr("schedkey").ToString.Trim)
Loop

Error_Exit:
If Not IsNothing(rdr) Then
rdr.Close()
cmd.Connection.Close()
cmd.Dispose()
cnn.Close()
cnn.Dispose()
End If

rdr = Nothing
cmd = Nothing
cnn = Nothing
Exit Sub

Error_Handling:
Select Case Err.Number
Case Else
MsgBox("The following error occured in LoadSchedules()" & vbCrLf &
Err.Description)
End Select

Resume Error_Exit
 
R

rowe_newsgroups

I have a simple subroutine (see below) which reads a table and populates the
items of a combobox. As the subroutine is executed, I'm watching the
taskmanager and the memory in use increases; however, once I get to the
Error_Exit: logic and start to close and dispose my memory variables, the
memory is not released and does not decrease in the task manager. Why is
that? Eventually, my program is giving me "Insufficient memory" errors.

****************************************************************************************

On Error GoTo Error_Handling

Dim cnn As New Odbc.OdbcConnection
Dim cmd As New Odbc.OdbcCommand
Dim rdr As Odbc.OdbcDataReader

cnn.ConnectionString = "dsn=" & accpac_companyid
cnn.Open()
cmd.Connection = cnn
cmd.CommandText = "select schedkey, scheddesc from cssktb order by schedkey"
rdr = cmd.ExecuteReader

Do While rdr.Read
combo.Items.Add(rdr("schedkey").ToString.Trim)
Loop

Error_Exit:
If Not IsNothing(rdr) Then
rdr.Close()
cmd.Connection.Close()
cmd.Dispose()
cnn.Close()
cnn.Dispose()
End If

rdr = Nothing
cmd = Nothing
cnn = Nothing
Exit Sub

Error_Handling:
Select Case Err.Number
Case Else
MsgBox("The following error occured in LoadSchedules()" & vbCrLf &
Err.Description)
End Select

Resume Error_Exit

Ugh - don't take this personal, but I hate your code, it brings back
unpleasant memories from my VB6 days. Anyways, I rewrote your using
Using blocks to dispose of the database objects, and replaced the
class VB style of error handling with .Net's try...catch blocks. I ran
a similar version (had to change it to connect to my SqlServer 2000
db) in an infinite loop and my memory never increased more than a few
100 kbs. Let me know if what your program does with the changes:

//////////////////////
Try
Using conn As New OdbcConnection(String.Format("dsn={0}",
accpac_companyid)), com As OdbcCommand = conn.CreateCommand
conn.Open()
com.CommandText = "selecte schedkey, scheddesc from
cssktb order by schedkey"

Using dr As OdbcDataReader = com.ExecuteReader()
While dr.Read()

combo.Items.Add(dr("schedkey").ToString().Trim())
End While
End Using
End Using
Catch ex As OdbcException
MsgBox(String.Format("The following ODBC error occurred in
LoadSchedules() {0} {1}", Environment.NewLine, ex.Message))
Catch ex As Exception
MsgBox(String.Format("The following error occurred in
LoadSchedules() {0} {1}", Environment.NewLine, ex.Message))
End Try
//////////////////////
 
R

rowe_newsgroups

Ugh - don't take this personal, but I hate your code, it brings back
unpleasant memories from my VB6 days. Anyways, I rewrote your using
Using blocks to dispose of the database objects, and replaced the
class VB style of error handling with .Net's try...catch blocks. I ran
a similar version (had to change it to connect to my SqlServer 2000
db) in an infinite loop and my memory never increased more than a few
100 kbs. Let me know if what your program does with the changes:

//////////////////////
Try
Using conn As New OdbcConnection(String.Format("dsn={0}",
accpac_companyid)), com As OdbcCommand = conn.CreateCommand
conn.Open()
com.CommandText = "selecte schedkey, scheddesc from
cssktb order by schedkey"

Using dr As OdbcDataReader = com.ExecuteReader()
While dr.Read()

combo.Items.Add(dr("schedkey").ToString().Trim())
End While
End Using
End Using
Catch ex As OdbcException
MsgBox(String.Format("The following ODBC error occurred in
LoadSchedules() {0} {1}", Environment.NewLine, ex.Message))
Catch ex As Exception
MsgBox(String.Format("The following error occurred in
LoadSchedules() {0} {1}", Environment.NewLine, ex.Message))
End Try
//////////////////////

Silly me I forgot to add:

////
Thanks,

Seth Rowe
////

To the end of my message (not that it really matters :))

Thanks,

Seth Rowe
 
G

Guest

I wouldn't trust task manager memory reporting. There are numerous reports
on this site regarding this.

Your code looks like vb6 migrated minimally as possible to compile in vb.net.
I think that you would benefit by recoding per MSDN examples. The only thing
that jumps out at me would be that your error trap checks if rdr is nothing.
This
occurs after the connection is opened and command created. You don't close
these if the failure happens before the reader creation. I suspect this is
not
happening seems to work other than memory issues.
 
J

Jerry

Thanks Seth;
I used your example and it turns out to be a bit thinner on the memory usage
(12kb - if you believe Task Manager) so I will go with that and see if it
helps. If anything, it's significantly less coding and I like the error
handling (ODBC vs general exceptions).

I've tried profiling the code with AntsProfiler (and many others), but I
have NO IDEA what I'm looking at there. Am I wrong in assuming the memory
in use should return to the value before the subroutine executes if all the
variables are terminated properly?

Thanks again.
 
M

Miro

The task manager shows an incorrect memory usage.

I once had a great link to an article
( similar to this one: http://www.itwriting.com/dotnetmem.php )

but it showed a step by step example with a hello world example.

Basically Windows automatically assigns "A Lot" of memory to an application, and
then if its needed it takes it away.
Something to do with "its easier to assign in the beginning, and take away later", than
adding more mid process.

M.
 
J

Jack Jackson

No, memory will not return to what it was when a method was invoked
when the method exits. .NET has a garbage collector. Memory will not
be freed until it is needed.
 
T

Tom Shelton

Thanks Seth;
I used your example and it turns out to be a bit thinner on the memory usage
(12kb - if you believe Task Manager) so I will go with that and see if it
helps. If anything, it's significantly less coding and I like the error
handling (ODBC vs general exceptions).

I've tried profiling the code with AntsProfiler (and many others), but I
have NO IDEA what I'm looking at there. Am I wrong in assuming the memory
in use should return to the value before the subroutine executes if all the
variables are terminated properly?

Thanks again.

Several things to note...

The lifetime of a .net object is non-deteministic, due to the nature
of the garbage collector. The only thing that can be said, is that
the reference will be freed at some point after there are no more
active references. That being said, there is no point in setting
references to nothing at the end of your procedure, since they will be
out of scope and elligible for colleciton anyway.

The side affect of this is how to deal with unmanaged resources
(unmanaged meaning that they exist outside the scope of the runtime),
such as system handles, database connections, sockets, etc - since you
often want these sort of resources to behave in a deterministic way...

The answer to that is the use of the disposable pattern. Objects,
that contain resources that should be released immediately, should
implement the IDisposable interface - which contains the single method
Dispose. This is a signal to you that this object needs some
deterministic cleanup, and you should ensure that it is called when
you no longer need the object. The easiest way to do this is to scope
the object in a Using block where possible, since this will ensure
that Dispose is called even if an error is raised.

As for your memory usage... The taskmanager is not a good way to
profile memory. There are lots of little tricks that windows plays to
make memory allocation quicker (like allocating a large initial
working set, not immediately releasing memory from a process when it
has been deallocated, etc) that are not immediately apparent by
looking at the taskmanager. You should really be looking at the
performance monitor or a .net profiler...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top