Finding memory leak

I’m chasing a memory leak at the moment by commenting out code. Is there a better way to find the problematic code?

  • Checking if destructors are being called when they should (by printing with System.DebugLog to the console).
  • For UI elements, checking them after closing in the debugger’s runtime object list (this only works if the project is small and you don’t use introspection which “pollutes” this list).

In general I think it depends very strongly on the architecture of the application. The more decoupled the objects are and the less global state there is, the easier it seems to me to get the part of the code leading to a memory leak.

There is no UI involved. The memory leak is happening where I add lots of records to a database. Checking the destructors is a good idea. Since I’m shifting around GBs of data there is now a global module holding the data. I’ll check that out, too.

For our console-based server, I created a runtime objects report that shows the number of each type of object and compares that to the last time the report was run. We caught a few leaks that way.

I’m mostly out today but will post the code as soon as I can if you think it would be helpful.

Actually, I had forgotten that my original code had been refactored into something cleaner using objects, but this is the meat of it.

  //
  // Refresh the report with the latest objects in memory
  //
  
  
  dim report as new Dictionary
  
  ObjectCount = Runtime.ObjectCount
  MemoryUsed = Runtime.MemoryUsed
  
  //
  // Since we are getting a reference to every object, 
  // any objects that rely on their destructors firing in
  // the Thread that created them may be thwarted. By
  // preventing background tasks, we should avoid that.
  //
  
  #pragma BackgroundTasks False
  
  dim iterator as Runtime.ObjectIterator = Runtime.IterateObjects
  while iterator.MoveNext
    dim o as object = iterator.Current
    dim ti as Introspection.TypeInfo = Introspection.GetType(o)
    
    dim key as variant = ti.FullName
    dim status as ObjectStatusItem = report.Lookup(key, nil)
    
    if status is nil then
      status = new ObjectStatusItem
      status.ClassName = key
      
      report.Value(key) = status
    end if
    
    status.InstanceCount = status.InstanceCount + 1
  wend
  
  iterator = nil
  
  #pragma BackgroundTasks True
  
  if mLastReport isa Dictionary then
    //
    // Look for items in the old report and compare the instance counts
    // tracking the change
    //
    for each key as String in mLastReport.Keys
      dim thisStatus as ObjectStatusItem = report.Lookup(key, nil)
      dim oldStatus as ObjectStatusItem = mLastReport.Value(key)
      
      if thisStatus isa ObjectStatusItem then
        thisStatus.LastInstanceCount = oldStatus.InstanceCount
      end if
    next
  end if
  
  redim Items(-1)
  dim names() as String
  
  for each key as String in report.Keys
    names.Append key
    Items.Append report.Value(key)
  next
  
  names.SortWith(Items)
  
  mLastReport = report

One caveat: If you use any classes whose Destructor is responsible for calling CriticalSection.Leave, running this report will cause your app to come to throw an exception at best or come to a grinding halt at worst. Semaphores won’t have that issue.

@Kem Tekinay : thanks, I’ll try this out. One big culprit seems to be updating a dock image from a class. The MBS example uses a timer and this works fine. Doing the same with a class gives me a big memory leak. But that doesn’t seem to be all.

Forum for Xojo Programming Language and IDE. Copyright © 2021 Xojo, Inc.