Help with memory leak - timer.performWithDelay

Hi all,

I’m not sure why the following example code doesn’t free up all the timer memory. It starts at around 133kb, if you tap the screen it creates 10,000 timers. After they have run, they should be cleaned up. However after removing all the timers, it levels out at about 389kb.

Am I missing something?

– main.lua
[lua]local removeTimer = false
local timers = {}

Runtime:addEventListener(“touch”, function(e)
if(e.phase == “began”) then
print(“TIMERS CREATED”)
timers = {}
for i=1,10000 do
table.insert(timers, timer.performWithDelay(3000, function(e)
removeTimer = true
end, 1))
end
end
end)

Runtime:addEventListener(“enterFrame”, function(e)
if(removeTimer) then
print(“KILLED TIMERS”)
for i=1,#timers do
local aTimer = timers[i]
timer.cancel(aTimer)
timers[i] = nil
end

timers = nil
removeTimer = false
end

collectgarbage(“collect”)
print( "MemUsage: " … collectgarbage(“count”) )
end)[/lua]

Thanks,

Aaron [import]uid: 118390 topic_id: 33111 reply_id: 333111[/import]

One issue that *could* be occurring is that the touch event has not completed before the enterframe event fires. If that happens, some timers will still be in the process of instantiation while the enterframe starts clearing out a small portion (however many were in the table when the enterframe fired off). Note that the enterframe only removes #timers, not 10,000.

Perhaps if the enterframe waited until there were 10,000 entries in the timer table, that would change the results? [import]uid: 79933 topic_id: 33111 reply_id: 131473[/import]

Hmm it was worth a shot, but unfortunately it didn’t make a difference:

[lua]local timersFired = 0
local timers = {}
local maxTimers = 10000

Runtime:addEventListener(“touch”, function(e)
if(e.phase == “began”) then
print(“TIMERS CREATED”)
timers = {}
for i=1,maxTimers do
table.insert(timers, timer.performWithDelay(3000, function(e)
timersFired = timersFired + 1
end, 1))
end
end
end)

Runtime:addEventListener(“enterFrame”, function(e)
if(timersFired == maxTimers) then
print(“KILLED TIMERS”)
for i=1,maxTimers do
local aTimer = timers[i]
timer.cancel(aTimer)
timers[i] = nil
end

timers = nil
timersFired = 0
end

collectgarbage(“collect”)
print( "MemUsage: " … collectgarbage(“count”) )
end)[/lua] [import]uid: 118390 topic_id: 33111 reply_id: 131475[/import]

Well, I don’t see anything else… It could be a memory leak, or could be an oddity of the implementation exposed by your test harness.

One such oddity could be that the timers aren’t actually removed on the cancel timer call, but just disabled from firing their events, and when they do count down, THEN the SDK removes them. If that were the case, delaying the memory check (not removal) until after they have fired off would expose it.

But that’s not particularly likely (but possible). I’d say it’s more likely there’s some other issue (like lua is not garbage collecting the timers array until it leaves that function, or some other idiosyncrasy), or an actual memory leak… [import]uid: 79933 topic_id: 33111 reply_id: 131478[/import]

Thanks for the advice. I don’t think it’s the first issue, the memory is printed on every frame. After they are removed it reports the same number indefinitely. My only guess is corona has kept some of the timer in memory, maybe to cache it in case I create another one later. It’s just annoying as im using the cider profiler, and the graphical memory tree steadily rises throughout playing my game. The timer leak makes it difficult to find other leaks. [import]uid: 118390 topic_id: 33111 reply_id: 131489[/import]

Quick update, seems this is some sort of internal lua thing. If I create 8192 listeners, the memory settles at 261kb.

1 more listener, 8193, it settles as 389kb.

I guess when the table is created it allocates itself a chunk in memory, even when the table is destoryed that memory is kept. If I re-create and re-destory the timers a second, third or fourth time, it continues to settle at the same memory amount - so it’s re-using that allocated space in memory. [import]uid: 118390 topic_id: 33111 reply_id: 131497[/import]

Sounds like you’re getting a grip on it, or at least boxing in the behavior…

the internal mechanisms of creating the timer/allocating resources for it by lua/corona/;the OS might not be attached to the apps garbage collect. Can’t think of a way to easily tell… It could be the timer stuff allocates nothing until first called, then allocates 8192 blocks as needed (but never releases the initial one, in case you use timers again).

8192 does sound like a traditional block mem size to allocate for a system resource thats pooling its allocations though. [import]uid: 79933 topic_id: 33111 reply_id: 131504[/import]

One issue that *could* be occurring is that the touch event has not completed before the enterframe event fires. If that happens, some timers will still be in the process of instantiation while the enterframe starts clearing out a small portion (however many were in the table when the enterframe fired off). Note that the enterframe only removes #timers, not 10,000.

Perhaps if the enterframe waited until there were 10,000 entries in the timer table, that would change the results? [import]uid: 79933 topic_id: 33111 reply_id: 131473[/import]

Hmm it was worth a shot, but unfortunately it didn’t make a difference:

[lua]local timersFired = 0
local timers = {}
local maxTimers = 10000

Runtime:addEventListener(“touch”, function(e)
if(e.phase == “began”) then
print(“TIMERS CREATED”)
timers = {}
for i=1,maxTimers do
table.insert(timers, timer.performWithDelay(3000, function(e)
timersFired = timersFired + 1
end, 1))
end
end
end)

Runtime:addEventListener(“enterFrame”, function(e)
if(timersFired == maxTimers) then
print(“KILLED TIMERS”)
for i=1,maxTimers do
local aTimer = timers[i]
timer.cancel(aTimer)
timers[i] = nil
end

timers = nil
timersFired = 0
end

collectgarbage(“collect”)
print( "MemUsage: " … collectgarbage(“count”) )
end)[/lua] [import]uid: 118390 topic_id: 33111 reply_id: 131475[/import]

Well, I don’t see anything else… It could be a memory leak, or could be an oddity of the implementation exposed by your test harness.

One such oddity could be that the timers aren’t actually removed on the cancel timer call, but just disabled from firing their events, and when they do count down, THEN the SDK removes them. If that were the case, delaying the memory check (not removal) until after they have fired off would expose it.

But that’s not particularly likely (but possible). I’d say it’s more likely there’s some other issue (like lua is not garbage collecting the timers array until it leaves that function, or some other idiosyncrasy), or an actual memory leak… [import]uid: 79933 topic_id: 33111 reply_id: 131478[/import]

Thanks for the advice. I don’t think it’s the first issue, the memory is printed on every frame. After they are removed it reports the same number indefinitely. My only guess is corona has kept some of the timer in memory, maybe to cache it in case I create another one later. It’s just annoying as im using the cider profiler, and the graphical memory tree steadily rises throughout playing my game. The timer leak makes it difficult to find other leaks. [import]uid: 118390 topic_id: 33111 reply_id: 131489[/import]

Quick update, seems this is some sort of internal lua thing. If I create 8192 listeners, the memory settles at 261kb.

1 more listener, 8193, it settles as 389kb.

I guess when the table is created it allocates itself a chunk in memory, even when the table is destoryed that memory is kept. If I re-create and re-destory the timers a second, third or fourth time, it continues to settle at the same memory amount - so it’s re-using that allocated space in memory. [import]uid: 118390 topic_id: 33111 reply_id: 131497[/import]

Sounds like you’re getting a grip on it, or at least boxing in the behavior…

the internal mechanisms of creating the timer/allocating resources for it by lua/corona/;the OS might not be attached to the apps garbage collect. Can’t think of a way to easily tell… It could be the timer stuff allocates nothing until first called, then allocates 8192 blocks as needed (but never releases the initial one, in case you use timers again).

8192 does sound like a traditional block mem size to allocate for a system resource thats pooling its allocations though. [import]uid: 79933 topic_id: 33111 reply_id: 131504[/import]