How to track down a Memory Leak

I’m running into an OutOfMemoryException while testing my app on a 27" retina iMac and while the error is triggered by maximizing my app’s main window canvas to full screen, I know it can render the image that size in other instances so I suspect the problem is due to a memory leak somewhere else due to poor coding on my part.

So 1) What’s the best way to track down a memory leak? and 2) Can anyone clarify whether the following scenario could be the culprit?

In a method I create a local method-only picture array and append new picture objects to that array. In the same method I store a reference some of those array picture objects in a global dictionary in the form dic.value(i) = pic array(i) etc.

When the method ends, will the picture array destruct completely or could the issue be that it doesn’t since the global dictionary is referencing some of the array objects? If so, would I have to clone the array picture objects and put the clones into the dictionary to resolve this?

Yes, you understand the problem. An easy solution is to try storing WeakRefs instead in the Dictionary. You could clone the pictures themselves, but that would use much more memory.

See http://developer.xojo.com/WeakRef

For ease of use, it’s often handy to set this up as a computed property so you don’t have to worry about getting the WeakRef.value even time.

Hi Michael. Thanks for your reply. I’ve never heard of WeakRef before and am a little confused of how to implement it. Firstly, is this just a method to investigate references to objects hanging around so I can then act upon this in some way, or are you saying I should somehow use this instead of a global dictionary to store the referenced image objects and this will solve the memory leak issue…if this is indeed the thing causing it?

OutOfMemoryException can be triggered by a loop that consumes memory until there is no more. It can be caused by an actual, malformed loop, or by a circular reference.

Another source of that particular exception : recursion.

Have you tried to look at the stack to approximately locate the source of the problem ?

http://developer.xojo.com/outofmemoryexception

Hi Michel. I don’t think it’s a loop issue or a recursion issue because I try not to use recursive calls where possible and the error stack doesn’t flag anything obvious. My suspicion is circular references caused by global dictionaries storing references to multiple large pictures in local arrays.

Are there not any Xojo utilities that can examine a running app and point to circular references or even memory leaks?

I usually use Instruments for speed profiling. But the app can also do checks for memory leaks and zombies (whatever this is).

So it would seem that when the function ends the array is wound down, BUT you still have a references to the pictures in the dictionary so the actual pictures are not getting released. If you don’t need them anymore, you can simply nullify the dictionary at the end of the method. If you need them at the end, then you need to do one of several things.

  1. Do you need ALL of these pictures?
  2. Do they need to be the size that you’ve created them at?
  3. Can you build 64-Bit, allowing the application to use more than 4gb of RAM.
  4. Is your application loading these pictures or did you drag them into the project (only makes a difference if you let them go).

Hi Sam. In answer to your questions:

  1. Yes
  2. Yes.
  3. No.
  4. App is creating them in code.

To help explain the issue I’ve put together a very simplified version of the method I’m using that I think is causing the problem:

[code]Sub UpdateCanvas(ByRef g As Graphics, X As Integer, Y As Integer)

Dim p, picArray(-1) As Picture
Dim i, count As Integer
Dim obj As CustomObject

// Reset this new dictionary of picture objects
picsChangedGlobalDic = New Dictionary

For i = 0 to 100

// If pic has changed in any way then create a new one otherwise use the old one
If PictureHasChanged() then
p = New Picture (width, height)
Else
p = CustomObject(OldGlobalDic.value(i)).Pic
End If

// Append new picture (or old picture reference) to local array
picArray.Append§

// If picture has changed then draw it and store it
If PictureHasChanged() then
g.DrawPicture(UBound(picArray), X, Y)
picsChangedGlobalDic.value(i) = UBound(picArray)
End If

// Increment values as required for next iteration
X = X + 10
Y = Y + 10

Next

// Reset values
X = 0
Y = 0

// Reset global dictionary after it has been used in above
// loop to retrieve unchanged pictures from last execution
OldGlobalDic = New Dictionary

count = UBound(picArray)

// Loop through pic array and store all lines (old and
// new) in global dictionary for use elsewhere in app
For i = 0 to count

obj = New CustomObject

obj.X = X
obj.Y = Y
obj.Pic = picArray(i)

OldGlobalDic.value(i) = obj

// Increment values as required for next iteration
X = X + 10
Y = Y + 10

Next

End Sub[/code]

I don’t except in that method using picsChangedGlobalDic = New Dictionary and OldGlobalDic = New Dictionary since I expected local picArray to destroy at end of method etc.

instead of

 dic.value(i) = pic array(i) etc.

you would simply do

 dic.value(i) = new WeakRef(PicArray(i))

and then when you want to get the object back, you do this:

  dim wr as WeakRef = dic.value(i)
  dim p as picture
  if wr.value <> nil then
      p = Picture(wr.value)
  end if

Using this technique, your Dictionary will hold a reference to your pictures, but it’s a Weak reference: it will not keep the pictures around if you have released them elsewhere.

In other words, a WeakRef is a way to hold a reference to an object without preventing it from being destructed.

So I’ve read your code a couple of times and I think I understand what you’re doing with it. You need to keep all the images as a form of cache. But you are resetting the cache on every canvas update.

Instead you might want to consider splitting it into two functions.

  1. Is for ensuring the cache is upto date.
  2. Is simply for drawing the cache to the context.

This way any display update to the canvas (i.e. window resized, activated, and so forth) is quick as it only draws the pictures. Meanwhile when your data changes, your image caches changes and then you can call canvas.invalidate to draw the pictures.

Also looking at this code, you might find it better to use an array of custom objects v.s. a dictionary, it seems all your data is sequential, at least from this sample.

Also in the sample code, you create a picture with the a width and height property, I would use different names just so that your code doesn’t get confused and creates pictures that are the width and height of the canvas, when I’m assuming their only 10 x 10.

Really??

Thanks for explaining. I understand that now but I think in this situation it will not work for what I need to achieve because as Sam correctly assumed, I need to store the pictures from the local pic array in a global cache so on the next execution of the method I can redim the local array but still refer to the global cache when creating the new array to determine what has changed.

[quote=331957:@Sam Rowlands]So I’ve read your code a couple of times and I think I understand what you’re doing with it. You need to keep all the images as a form of cache. But you are resetting the cache on every canvas update.

Instead you might want to consider splitting it into two functions.

  1. Is for ensuring the cache is upto date.
  2. Is simply for drawing the cache to the context.

This way any display update to the canvas (i.e. window resized, activated, and so forth) is quick as it only draws the pictures. Meanwhile when your data changes, your image caches changes and then you can call canvas.invalidate to draw the pictures.
[/quote]

Correct. As clarified above I need to keep a global cache of images to compare to next time the method is executed. Because a canvas resize would mean the picture has changed it would not be possible to split this up into two methods and the actual method is very long and complex and kind of needs to be handled all at once.

I’m not sure I can do this because I use the dictionary so I can perform a super-fast lookup using If dic.HasKey(i) etc and if I use an array of custom objects I suspect I would have to iterate it somehow? Or could I use IndexOf perhaps?

That was just for simplification but I am using specified width and height parameters when creating the new picture object.

[quote=331959:@Alfred Van Hoek]g.DrawPicture(UBound(picArray), X, Y) picsChangedGlobalDic.value(i) = UBound(picArray)Really??[/quote]

Yes, actually the PictureHasChanged method in reality calls another method and passes the picArray ByRef and that method determines whether the picture has changed or not and appends either a new picture object or a cached reference to one to the array so when the execution returns back to the main method I have to go by the last object appended to the array.