Memory Leak Issue and Strange Exception Error

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?

Looking at that exception, I suspect the control is becoming Nil before we get a chance to send a command to the browser. Please file a bug report with that stack trace and a subject of “NilObjectException in WebControl.ExecuteJavascript”

Thanks, Greg. Will do.

Do you think this is what is causing the memory leak? I put a timer in my app object to count the number of sessions every 30 seconds and after a session is closed, that number never drops so there’s a reference to the sessions hanging around somewhere. I wonder if this exception is preventing the sessions from closing. But it doesn’t happen 100% of the time. Yet, I can’t find where I have any references to the sessions that are not just local to a particular page or method on that page…

Report filed: <https://xojo.com/issue/36213>

[quote=139250:@Jon Ogden]Thanks, Greg. Will do.

Do you think this is what is causing the memory leak? I put a timer in my app object to count the number of sessions every 30 seconds and after a session is closed, that number never drops so there’s a reference to the sessions hanging around somewhere. I wonder if this exception is preventing the sessions from closing. But it doesn’t happen 100% of the time. Yet, I can’t find where I have any references to the sessions that are not just local to a particular page or method on that page…[/quote]
At first glance I’d say no. I have a feeling that the fact that the exception occurs means that the session is closing and that the control or session is The thing that is Nil. I’ll be looking at this as soon as I get to my desk today.

OK. Thanks. Something is definitely keeping my sessions open. Perhaps some mangling of my code somewhere has revealed this issue. But for the life of me, I cannot find where I’m keeping a reference to the sessions open…

Greg -

I see from my feedback report that this issue is fixed. That’s great so I look forward to the next release.

Am I seeing the error due to something I am doing wrong?

I just noticed in my web app running 2014R2.1 that the close events in various controls are not firing. The page goes away but the event hasn’t fired. In some cases - nothing happens. In others, the SessionShutdownThread tries to clean things up but the page has already closed which is what seems to be raising the NIL exception.

Actually, I think the problem is an interaction between the thread pool manager you’ve implemented and the web framework. What’s happening is that a response is being formulated to be sent to the browser and somewhere in the middle of that process, the Session is going away. So we get all the way to the end and say “ok session, here’s what you need to send,” except, now the Session is Nil.

That’s very interesting then because the thread pool manager thread is basically suspended at this point. In the configuration for this customer it does one very small thing when the app first starts and then it’s done. No code on any page directly interacts with the ThreadPoolManager. Any threads it spawns are all launched directly from the App object and are all unrelated to any pages. So it’s strange that a session shutdown thread would interact with a suspended thread.

Maybe there’s an issue with the session shutdown thread interacting with suspended threads in general? Maybe that’s where the problem is. Almost has to be since the thread pool manager is doing absolutely nothing.

But it is sitting there looping, right? Waiting for the next request?

No. It’s suspended until I queue the next thread pool item. The queue method resumes the manager thread when a new item is added.

So it’s not looping at all once there’s no more work to be done.

Jon, do you have somewhere a threadsleep in your code? IIRC i have seen this before, that the thread went to sleep which in effect is a return to the main thread, but the thread then looses it’s context. Could that be your problem?

Nope. It’s suspended. At the point where the session shutdown thread is crashing the ThreadPoolManager is doing nothing. Completely suspended.

Here’s another data point.

Since this customer’s configuration does not use any threading beyond the initial use of the thread pool when the app first starts up, I basically have disabled the thread pool manager. I have verified that it is not running and have seen only the following items when pausing execution:

  • Main Thread
  • _SessionShutdownThread

Now I open several pages which creates multiple sessions. I let them all get loaded. I then close one of the pages. BAM! SessionShutdownThread breaks execution of the program. Except this time, I don’t see the break point with the little bug anywhere because everything in the SessionShutdownThread is hidden from me as that is Xojo’s code. I resume operation. I see the notice in the log like such:

NilObjectException Exception Message: Exception Error Number: 0 An exception of class NilObjectException was not handled. The application must shut down.

So the Nil object exception is still being thrown in the Xojo code. And after resuming, my session count is still not dropping. It is staying up there. So the sessions are being retained.

Now I close a few more windows. Nothing happens. No crash or break in the app. So I go back to Xojo and check my session count. It’s still at 4 which was the original number but now I am down to just one page open. So I should have one session (I’m reporting sessions in the log via a timer).

Now I decided to pause execution and see if I notice anything. Yes, the _SessionShutdownThread is gone. Not there in the list of threads.

So it sounds like this Nil object exception is killing the shutdown thread.

And keep in mind - the ThreadPoolManage thread is not running at all. I kill it upon startup.

And I have confirmed that the Nil Object Exception of the _SessionShutdownThread is killing the _SessionShutdownThread. Once that is thrown, the thread is killed and that would explain why it doesn’t work any longer and why no sessions are removed.

Here’s the stack trace from the exception that just occurred on me. This happened when I refreshed the web page:

[code]NilObjectException

RaiseExceptionClass
RaiseNilObjectException
WebControl._ExecuteJavascript%%osb
WebControl.ExecuteJavaScript%%os
WebControl._UpdateEventHandlers%%oA1ssb
WebImageView.!_AddHandlerCallback%%ossb
rbframework.dylib$2519
RuntimeRemoveEventHandler
WebGraphics.Destructor%%o
rbframework.dylib$2500
WebCanvas.__Exit
rbframework.dylib$2526
rbframework.dylib$2500
RuntimeLockUnlockObjects
WebSession._Cleanup%%o
_SessionShutdownThread.Event_Run%%o<_SessionShutdownThread>
rbframework.dylib$1100
_pthread_body[/code]

So none of this is in my code…Not sure what I am doing that throws this…

Okay, we’ve isolated this issue and a fix has been checked in for the exception, but I don’t think this is causing a memory leak.

Thanks, Greg.

I look forward to whenever the next release comes out. Since you say you don’t think this is causing the memory leak, I ended up changing a bunch of my session references to WeakRefs in the hope that using WeakRefs would help solve the memory leak issue. Unfortunately, not. And after the crash, things start getting wonky anyhow. So I probably am not going to be able to trace this down until I’m able to test with the fix. So now to wait patiently! :slight_smile:

Have you tried your project in the betas?

You know I can’t comment on any beta related activities outside the beta forums.