General memory leak principles? (Lua)

I’ve bookmarked some great techniques here (thanks, Matt!) for tracking down memory leaks, but on a high level, what kinds of culprits should one look for?

For instance is .new anything without later setting it to nil or re-using it a no-no?

(I’ve tried to make every image and samplePlayer in my game be a declared variable that gets re-used in case that helps. It seemed to.)

Or are memory leaks always SDK bugs? I’ve seen some suggest that, in which case I’d stop trying to chase the issue down and focus instead on a) using less RAM in general and b) making sure progress gets preserved through crashes. (Both of which should be pretty simple.)

My memory usage bounces up and down wildly but it averages out to a slooooooow leak that eventually causes a crash. It’s very unpredictable (my game is complex and randomized) and can take several hours, so most players might not see it… but I’d love NOBODY to see it!

(Is a slow leak a side effect of using the malloc map at all? My long-run automated stress-testing is on the Simulator rather than the device. It’s harder to test slow leaks on the device.)

Thanks in advance!

2 Likes

In addition to the .new -methods I'd look for curly braces. Whenever you write { }, you allocate a new table.

One common pitfall is string concatenation. Lua caches both the operands and the resulting string, meaning a simple on-screen timer can become a memory leak if you're not careful.

I'm not sure how smart the Lua GC actually is, but it might be that circular references don't get cleaned. Setting the references nil should remove any doubt.

1 Like

Thanks—two great tips!

I have tons of string concatenation, so I’ll look into what might be happening there. (From a little research, it looks like if I’m building a string in a loop, I might be better off building a table instead, converting it to a string one time at the end.)

Does a local table in a function (like this example) need to be manually disposed of to prevent memory being occupied forever each time the function is called?

If so, what’s the best way? Set it to {} at the end of the function? Set it to nil?

function doSomething()
     local myTable = {globalValuea, globalValueb, globalValuec}
     -- Do stuff with the table
     return someResult
end

(I’m not seeing a specific problem—my leak(s) are slow and unpredictable—I’m just looking for {} braces and seeing where I should be doing more! Trying to follow best practices.)

1 Like

I don’t fully grasp the mechanism, but I fixed the leak! Finally!

My global samplePlayer variables were not (reliably) releasing their assigned samples when I would re-use them with different sound files.

I found that my leak could be fixed simply by adding a .stop() right before the new sound’s .play().

Regardless, I’m still eager to get a better handle on the topic!

6 Likes