Re: memory leak using system.windows.forms.timer



Gestalt,

As you where giving typical problems as there are often with system.timers.timer,my first gues was that the problem was in that.

However the subject says clearly system.windows.forms.timer

However, in your code is the system.timer.timer

(Are you busy with a service, because that is for what the where the system.timers.timer is.

Cor

"Gestalt" <sspenning@xxxxxxxxx> wrote in message news:9a569c48-ead1-4b71-a820-5cb51d58be94@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
On Feb 11, 4:56 pm, Tom Shelton <tom_shel...@xxxxxxxxxxxxxxxxxx>
wrote:
On 2009-02-11, Gestalt <sspenn...@xxxxxxxxx> wrote:



> I'm working on a small application using the
> system.windows.forms.timer. The intent is to set an interval and on
> the tick event fire a function that returns the number of workstation
> objects that reside in a specific OU in Active Directory.

> On the surface the application runs great. Under the surface, it's a
> different story. I've found that every time the tick calls a trivial
> subroutine, memory leaks. I've tried summoning garbage collection
> periodically but that has not resulted in any improvement in memory
> consumption. Left overnight the app will bloat to over half a gig of
> RAM consumed (and continuing to climb). I have tested it on XP Pro,
> Vista SP1 (x64) and Windows Server 2008 (x86).

> The subroutine that appears to be leaking is here as follows:
> Private Sub CheckComputers()
> Dim intCount As Integer
> intCount = CountADComputers(My.Settings.strTargetDN)
> If intCount > My.Settings.intTargetCount Then
> intTrigger = 1
> Else
> intTrigger = 0
> End If
> intCount = Nothing
> End Sub

> I have found that if I substitute the call to the CountADComputers
> subroutine with a static number (e.g. 1) I still leak memory. If I
> leave this subroutine out of the program then the leak stops (and
> obviously the program does nothing).

What else does the timer do? Only calling this CheckComputers sub? It seems
highly unlikely that simply calling a sub is going to cause a memory leak.
This generally means unreleased references..

Hmmm... intTrigger - I dont see that declared in the timer routine, so is this
being monitored somewhere? another thread? Need more info :)

By the way, setting the intCount to nothing - is basically useless in the
above, and in fact will probably optimized out of the final code anyway.
There is almost no reason to ever explicitly set any object to nothing in
VB.NET (actually this was true in VB6 as well). The major exceptions are
module and class level fields - local values, well your mostly just wasting
your time and cycles :)



> For the record, here is the CountADComputers subroutine:
> Public Function CountADComputers(ByVal strADPath As String) As
> Integer
> Dim intCount As Integer
> Dim entry As New DirectoryServices.DirectoryEntry(strADPath)
> Dim mySearcher As New
> System.DirectoryServices.DirectorySearcher(entry)
> mySearcher.PageSize = 1000
> mySearcher.Filter = "(objectClass=Computer)"
> intCount = mySearcher.FindAll.Count
> mySearcher.Dispose()
> mySearcher = Nothing
> entry.Dispose()
> entry = Nothing
> Return intCount
> End Function

If your using .NET 2.0 (2005) or higher, then I believe I would write this
routine more like:

Public Function GetADComputers (ByVal strADPath As String) As Integer
Using entry As New DirectoryServices.DirectoryEntry(strADPath), _
mySearcher As new DirectoryServices.DirectorySearcher(entry)

With mySearcher
.PageSize = 1000
.Filter = "(objectClass=Computer)"
End With

Return mySearcher.FindAll.Count
End Using
End Function

If your not using 2005, then I might suggest using Try/Finally to ensure that
dispose is called even if an exception is thrown (the above is really
syntactic sugar for Try/Finally).



> Can anyone think of a reason why this would be causing a problem?

Not from the information/code provided. You might want to create a short, but
complete program (http://www.yoda.arachsys.com/csharp/complete.html) that
demostrates the issue. Or better yet, you might look at getting a memory
profiler tool - such as Ants Profiler. These kinds of tools are invaluable
when trying to track down stuborn memory leaks...

--
Tom Shelton

I've tried your suggestions and narrowed things down a bit.

First, I was wrong about the AD query not being the culprit. It is
the source of the leak. It turns out that my copy of Visual Studio
isn't always completing the build process when I tell it to do so.
Thus when I thought I was testing the code with AD query commented out
it was in fact still part of the cycle. I'm doing my dev from another
install now and the builds are coming out correctly.

I am evaluating the Ants Profiler app and I think it has identified
the source of the problem. Every time the Directory Searcher returns
new results, those results remain in memory even when the searcher is
disposed of. Garbage collection does not clean up the data, even when
called manually or through the Ants Profiler interface.

The only way I have found to mitigate this problem is to refine the
scope of my search to only return a single attribute from the computer
objects. This has slowed the growth in memory considerably but it has
by no means stopped it. I ran a test app overnight and it went from
7Meg RAM at start to 160Meg.

Here is a demonstrative app illustrating the problem. To test it an
active directory domain will need to be provided.

Imports System.Timers

Module Module1
Private MasterTimer As System.Timers.Timer
Dim strPath As String = "LDAP://<your domain here>"
Dim intTrigger As Integer = 0

Sub Main()
MasterTimer = New System.Timers.Timer(500)
AddHandler MasterTimer.Elapsed, AddressOf Tick
MasterTimer.Enabled = True
Console.WriteLine("Press the Enter key to exit the program.")
Console.ReadLine()
End Sub

Private Sub Tick(ByVal source As Object, ByVal e As
ElapsedEventArgs)
CheckComputers()
End Sub

Private Sub CheckComputers()
If GetADComputers(strPath) > 1 Then
Console.WriteLine("Alert State Detected: " & GetADComputers
(strPath) & vbCrLf)
intTrigger = 1
Else
Console.WriteLine("Situation Normal " & GetADComputers
(strPath) & vbCrLf)
intTrigger = 0
End If
End Sub

Public Function GetADComputers(ByVal strADPath As String) As
Integer
Using entry As New DirectoryServices.DirectoryEntry
(strADPath), mySearcher As New DirectoryServices.DirectorySearcher
(entry)
With mySearcher
.PageSize = 1000
.Filter = "(objectClass=Computer)"
.PropertiesToLoad.Add("sAMAccountName")
End With
Return mySearcher.FindAll.Count
End Using
End Function
End Module

Does anyone have any idea what I can do to stop the leak entirely?
Should I address this to the ADO group?

.



Relevant Pages


Loading