I have had a customer report to me an issue related to memory not being released when the page is closed. I have since verified this. I can open pages in my web app, see the memory increase and then not get released when the page is closed. It is a standalone web app. I have looked at all my objects and everything and I cannot find any references to pages that have been closed. None of my AddHandler directives reference anything outside the page they are on. None of my app properties are referencing anything on a page.
Initially I discovered a bug where I was keeping references to sessions but not destroying them when a session closed. I thought that was the source of my memory leak - having references to all the sessions in the app object. I eliminated that code but that didn’t solve the problem.
But I think I may have found the issue but I don’t know how to fix it. Or maybe it’s another issue and unrelated. So I am asking for some help here.
I see an occasional exception being raised that I just don’t understand. Let me explain.
I am using Aaron Ballman’s ThreadPoolManager class to implement some thread pooling in the App object of the web app. At the point in question of when these pages are closing, the ThreadPoolManager is basically idle. Here’s the run code from the ThreadPoolManager Thread:
// We want to loop forever; once there's a manager,
// there's always a manager
while Not KillMe
// Check to see if we've got any items in our work queue
if UBound( mWorkQueue ) >= 0 then
// Check to see if we've got any free threads in the pool
dim freeThread as ThreadPoolThread = GetFreeThreadFromPool
//TimesAsleep = 0
if freeThread <> nil then
// We have a thread free, so we want to tell it
// to do some work
freeThread.SetWorker( mWorkQueue( 0 ) )
// Remove the work item
mWorkQueue.Remove( 0 )
'System.DebugLog("About to run new Thread - Thread Number "+str(mWorkQueue.Ubound))
// And run the thread
freeThread.Run
Dim n as integer = 0
For i as integer = 0 to mThreadPool.Ubound
If mThreadPool(i).State <> Thread.NotRunning Then
n = n+1
End If
Next
If n > MaxNumThreads Then MaxNumThreads = n
else
// No free threads. We could boost the number of threads
// available in our pool. But we're just going to sleep for
// a while instead.
me.Sleep( 1000, true )
end if
else
// Check to see if the user has changed any of our
// hints or not
if me.mHintsChanged then
// We may need to resize our thread pool, or modify
// some priorities of non-running threads.
UpdatePool
end if
//TimesAsleep = TimesAsleep+1
//
//If TimesAsleep = 10 Then
//me.Suspend
//End If
#Pragma BreakOnExceptions Off
// We don't have any work to do - suspend
// ADDED TRY BLOCK BECAUSE OF THE EXCEPTION HERE THAT IS THROWN AFTER A PAGE CLOSES
Try
me.Suspend // ->>> This is where the exception is being thrown. And even with the Pragma
// ->>> the app still breaks.
Catch
Try
me.sleep(1000)
Catch
End Try
End Try
#Pragma BreakOnExceptions On
end if
wend
So where the Try block is at is where the exception occurs. So I get a Nil object exception. Why? The ThreadPoolManager exists (it’s a singleton). And it shows as existing in the debugger too. And the break happens even with the Pragma statement.
It happens after a page closes. The exception stack trace is below:
NilObjectException
RaiseExceptionClass
RaiseNilObjectException
WebControl._ExecuteJavascript%%o<WebControl>sb
WebControl.ExecuteJavaScript%%o<WebControl>s
WebControl._UpdateEventHandlers%%o<WebControl>A1ssb
WebImageView.!_AddHandlerCallback%%o<Object>ssb
rbframework.dylib$2519
RuntimeRemoveEventHandler
WebGraphics.Destructor%%o<WebGraphics>
rbframework.dylib$2500
WebCanvas.__Exit
rbframework.dylib$2526
rbframework.dylib$2500
RuntimeLockUnlockObjects
WebSession._Cleanup%%o<WebSession>
_SessionShutdownThread.Event_Run%%o<_SessionShutdownThread>
rbframework.dylib$1100
_pthread_body
It looks to me that it’s happening in the destructor method of WebGraphics. Looks like there’s an event handler not being removed correctly. So I think what is happening with my memory is this exception is being thrown and the objects are not really being destructed and cleaned up. They are still there in memory.
Might this be what is happening?
Do I need to file a bug report?