memory leak with touch and collision events

Hi Folks -

I have 4 different balls displayed and event listeners attached to the four balls. Then I have a board with 50 holes and those have a collision listener attached to every hole. I am checking my memory usage and it’s definitely leaking. While I have no global variables, (yay), but every time I touch a ball or a ball collides with a hole, memory usage increases.

I display the balls and holes at the start of the game, and do not remove or redisplay any of them. The touch and collision listeners are assigned when I display those items at the beginning.

Since I’m never removing the balls or holes from the screen how do I stop the memory leak?

Thank you!

P.S. I’m writing this on my phone so I don’t have access to the code at the moment.

[import]uid: 25787 topic_id: 7050 reply_id: 307050[/import]

Can you provide a test case that demonstrates the problem. We fixed an issue with removeSelf() and physics bodies that caused a memory leak, which was fixed in a recent daily build (what build are you using, btw?) But you say you’re not removing anything so that doesn’t sound like the problem. Need more info.
Tim [import]uid: 8196 topic_id: 7050 reply_id: 24718[/import]

Hi Tim -

I just updated Corona and am using the latest build (2011.268). I’ve pasted all of my code, there’s a lot of other functions in there, sorry it’s so long. The onTouch and Collision functions are nested in the startNewGame at the bottom.

I have touch listeners attached to two red balls and the two blue balls. And I have collision listeners attached to all of the red holes (called redBallHoles) and the blue holes (called redBlueHoles). The balls can be dragged along the screen and if they are released while touching the appropriate row (top row is for red balls, bottom row is for blue balls), then the ball is moved on top of that hole. If not, it goes back to where it started. Nothing ever gets destroyed or removed from the screen.

I have it set to print the memory count every time a ball is touched or whenever it collides with any holes. Memory usage increased approximately by 0.2kb every time a touch or collision occurs.

Thank you for your help.

[lua]local ballHolesCollisionFilter = { categoryBits = 1, maskBits = 6 }
local redBallCollisionFilter = { categoryBits = 2, maskBits = 1 }
local blueBallCollisionFilter = { categoryBits = 4, maskBits = 1 }

local r = display.newText("Red’s Score is ",60, 600, native.systemFont, 25) – display red’s score
local b = display.newText("Blue’s Score is ",60, 650, native.systemFont, 25) – display red’s score
local printRedScore = display.newText(“0”, 240, 600, native.systemFont, 25) – display red’s score
local printBlueScore = display.newText(“0”, 240, 650, native.systemFont, 25) – display red’s score
local function garbagePrinting()
collectgarbage(“collect”)
print(collectgarbage(“count”))
end

local function printScore(redBall, blueBall, redBallHoles, blueBallHoles)
local leadRedHole
local leadBlueHole
if redBall[1].currentHole > redBall[2].currentHole then – figure out which red ball is in the lead
leadRedHole = redBall[1].currentHole
else leadRedHole = redBall[2].currentHole
end

if blueBall[1].currentHole > blueBall[2].currentHole then – figure out which blue ball is in the lead
leadBlueHole = blueBall[1].currentHole
else leadBlueHole = blueBall[2].currentHole
end
printRedScore.text = redBallHoles[leadRedHole].value – update Red Ball score
printBlueScore.text = blueBallHoles[leadBlueHole].value – update Blue Ball score
end

local function holeCollisionTrue (collideTable, redBall, blueBall, redBallHoles, blueBallHoles) – this function handles moving the ball to the new hole
local holeNumber = collideTable[2]
local holeValue = collideTable[3]
local whichBall = collideTable[4]
local ballColor = collideTable[5]
if ballColor == “Red Ball” then – check ball is red
transition.to ( redBall[whichBall], {time=200, x = redBallHoles[holeNumber].x, y = redBallHoles[holeNumber].y} )
redBall[whichBall].previousHole = redBall[whichBall].currentHole – update the previous location to the current hole
local previousHole = redBall[whichBall].previousHole
redBallHoles[previousHole].state = true – update the previous hole to being empty
redBall[whichBall].currentHole = holeNumber – update the location of the ball
redBallHoles[holeNumber].state = false --update the current hole to being filled
elseif ballColor == “Blue Ball” then – check if ball is blue
transition.to ( blueBall[whichBall], {time=200, x = blueBallHoles[holeNumber].x, y = blueBallHoles[holeNumber].y} )
blueBall[whichBall].previousHole = blueBall[whichBall].currentHole – upate the previous location the current hole
local previousHole = blueBall[whichBall].previousHole
blueBallHoles[previousHole].state = true – update the previous hole to being empty
blueBall[whichBall].currentHole = holeNumber – update location of ball
blueBallHoles[holeNumber].state = false – update the curent hole to being filled
end
printScore(redBall, blueBall, redBallHoles, blueBallHoles)
garbagePrinting()
end

local function holeCollisionFalse (collideTable, redBall, blueBall, redBallHoles, blueBallHoles) – this function handles moving balls back to their previous hole
local holeNumber = collideTable[2]
local holeValue = collideTable[3]
local whichBall = collideTable[4]
local ballColor = collideTable[5]
if ballColor == “Red Ball” then – move red balls
local holeNumber = redBall[whichBall].currentHole
transition.to ( redBall[whichBall], {time=500, x=redBallHoles[holeNumber].x, y=redBallHoles[holeNumber].y} )
elseif ballColor == “Blue Ball” then – move blue balls
local holeNumber = blueBall[whichBall].currentHole
transition.to ( blueBall[whichBall], {time=500, x=blueBallHoles[holeNumber].x, y=blueBallHoles[holeNumber].y} )
end
end

local function holeCollisionBeginning (ball, redBall, blueBall, redBallHoles, blueBallHoles) – this function handles the first time a ball is moved and doesn’t touch any holes
local whichBall = ball.value
local ballColor = ball.myName
if ballColor == “Red Ball” then – move red balls
local holeNumber = redBall[whichBall].currentHole
transition.to ( redBall[whichBall], {time=500, x=redBallHoles[holeNumber].x, y=redBallHoles[holeNumber].y} )
elseif ballColor == “Blue Ball” then – move blue balls
local holeNumber = blueBall[whichBall].currentHole
transition.to ( blueBall[whichBall], {time=500, x=blueBallHoles[holeNumber].x, y=blueBallHoles[holeNumber].y} )
end
end

local function startNewGame ()

local physics = require (“physics”)
physics.start()
physics.setGravity( 0, 0 ) – overhead view, therefore no gravity vector
–physics.setDrawMode( “debug” )

local collideTable = {nil}
local redBallHoles = {nil}
local blueBallHoles = {nil}
local redBall = {nil}
local blueBall = {nil}

local function onTouch( event )
local ball = event.target – this is the ball being touched
local phase = event.phase
if “began” == phase then
– Make target the top-most object
local parent = ball.parent
parent:insert( ball )
display.getCurrentStage():setFocus( ball )
ball.isFocus = true
– Store ball’s initial position for moving purposes
ball.x0 = event.x - ball.x
ball.y0 = event.y - ball.y
elseif ball.isFocus then
if “moved” == phase then – “moved” means the ball is still being touched and moved

ball.x = event.x - ball.x0
ball.y = event.y - ball.y0
garbagePrinting()
elseif “ended” == phase then – “ended” means the the ball is no longer being touched
if ball.hitCount == 0 then – this is for when a ball is moved but doesn’t touch any holes and then is released
holeCollisionBeginning (ball, redBall, blueBall, redBallHoles, blueBallHoles)
display.getCurrentStage():setFocus( nil )
ball.isFocus = false
elseif ball.hitCount ~= 0 then
if collideTable[1] == true then – if the ball is still colliding with the hole
holeCollisionTrue(collideTable, redBall, blueBall, redBallHoles, blueBallHoles)
display.getCurrentStage():setFocus( nil )
ball.isFocus = false
elseif collideTable[1] == false then – if the ball is not colliding with the hole
holeCollisionFalse(collideTable, redBall, blueBall, redBallHoles, blueBallHoles)
display.getCurrentStage():setFocus( nil )
ball.isFocus = false
end
end
end
elseif “cancelled” == phase then
display.getCurrentStage():setFocus( nil )
ball.isFocus = false
end

– Important to return true. This tells the system that the event
– should not be propagated to listeners of any objects underneath.
return true
end

local function collision( self, event ) – Self is the hole and other is the ball. Event is the collision
local ballColor = event.other.myName – assign name of the ball (either “Red Ball” or “Blue Ball”)
local whichBall = event.other.value – which ball is being moved
local holeColor = self.myName --assign name of hole (either Red Ball Holes or Blue Ball Holes)
local holeState = self.state – is the hole empty or filled
local holeValue = self.value – this is the value of the hole
local holeNumber = self.number – this is the hole number (starter holes are 1 and 2)
local rightHole – boolean

– increase ball’s hit count so it will be able to go into a hole
if ballColor == “Red Ball” then
redBall[whichBall].hitCount = redBall[whichBall].hitCount + 1
elseif ballColor == “Blue Ball” then
blueBall[whichBall].hitCount = blueBall[whichBall].hitCount + 1
end

if event.phase == “began” then
– check if the ball is trying to go into the correct colored hole.
if (ballColor == “Red Ball” and holeColor == “Red Ball Holes”) or (ballColor == “Blue Ball” and holeColor == “Blue Ball Holes”) then
rightHole = true
else rightHole = false
end

if event.phase == “began” and rightHole == true and holeState then --only return true if collision has begun, the ball is colliding with the right colored hole and hole is empty
collideTable = { true, holeNumber, holeValue, whichBall, ballColor }
else
collideTable = { false, holeNumber, holeValue, whichBall, ballColor }
end
elseif event.phase == “ended” then
collideTable = { false, holeNumber, holeValue, whichBall, ballColor }
end
garbagePrinting()
end

– display board
local boardSize = { 920, 350 }
local boardX = (display.contentWidth - boardSize[1])/2 --x position of board’s top left corner
local boardY = (display.contentHeight - boardSize[2])/2 --y position of board’s top left corner
local board = display.newRoundedRect(boardX, boardY, boardSize[1], boardSize[2], 25) --draw cribbage board
board.strokeWidth = 5
board:setFillColor(0, 10, 10)
board:setStrokeColor(180, 180, 180)

– draw four starter holes
local ballHoleRadius = 10 --radius of ball hole
redBallHoles[1] = display.newCircle (boardX + 30, boardY + 70, ballHoleRadius)
redBallHoles[1].value = 0 – the starter holes don’t add to the score
redBallHoles[2] = display.newCircle (boardX + 30, boardY + 120, ballHoleRadius)
redBallHoles[2].value = 0 – the starter holes don’t add to the score
blueBallHoles[1] = display.newCircle (boardX + 30, boardY + 200, ballHoleRadius)
blueBallHoles[1].value = 0 – the starter holes don’t add to the score
blueBallHoles[2] = display.newCircle (boardX + 30, boardY + 250, ballHoleRadius)
blueBallHoles[2].value = 0 – the starter holes don’t add to the score

– draw red ball holes
local p = 2 – the first 2 holes in this array are for the starter holes
local j = 0
local f = 0
for h = 0, 2 do
for i = 0, 4 do
p = p +1
local redBallHoleX = 80 – ballhole x offset, 1st column
local redBallHoleY = 80 – ballhole y offset, 1st row
local x1 = boardX + redBallHoleX + 51*i + 260*h --51 is hole spacing in x (ballRadius*2 + ballHoleRadius*2 + 5)
local y1 = boardY + redBallHoleY + 55*j + 150*f --55 is hole spacing in y between rows 1 & 2 and 3 & 4
redBallHoles[p] = display.newCircle (x1, y1, ballHoleRadius)
physics.addBody( redBallHoles[p], “static”, { radius=ballHoleRadius, isSensor=true, filter = ballHolesCollisionFilter } ) – make ballHoles physics body and sensor
redBallHoles[p].isVisible = true --set to false to hide. Make sure collision is still active if holes are hidden
redBallHoles[p].number = p
redBallHoles[p].value = p-2 – hole value, starting with 1. Use this for the score
redBallHoles[p].myName = “Red Ball Holes”
redBallHoles[p].state = true --true means hole is empty, false means hole is filled with a ball
redBallHoles[p].collision = collision
redBallHoles[p]:addEventListener( “collision”, redBallHoles[p]) – add collision table listener to each hole sensor
end
end

– draw blue ball holes
local p = 2
local j = 0
local f = 0
for h = 0, 2 do
for i = 0, 4 do
p = p +1
local blueBallHoleX = 80 – ballhole x offset, 1st column
local blueBallHoleY = 130 – ballhole y offset, 1st row
local x1 = boardX + blueBallHoleX + 51*i + 260*h --51 is hole spacing in x (ballRadius*2 + ballHoleRadius*2 + 5)
local y1 = boardY + blueBallHoleY + 55*j + 150*f --55 is hole spacing in y between rows 1 & 2 and 3 & 4
blueBallHoles[p] = display.newCircle (x1, y1, ballHoleRadius)
physics.addBody( blueBallHoles[p], “static”, { radius=ballHoleRadius, isSensor=true, filter = ballHolesCollisionFilter } ) – make ballHoles physics body and sensor
blueBallHoles[p]:setFillColor (255,255,255)
blueBallHoles[p].isVisible = true --set to false to hide. Make sure collision is still active with hidden holes
blueBallHoles[p].number = p
blueBallHoles[p].value = p-2 – hole value, starting with 1. Use this for the score
blueBallHoles[p].myName = “Blue Ball Holes”
blueBallHoles[p].state = true --true means hole is empty, false means hole is filled with a ball
blueBallHoles[p].collision = collision
blueBallHoles[p]:addEventListener( “collision”, blueBallHoles[p]) – add collision table listener to each hole sensor

end
end

– display red balls
local p = 0
for n = 1, 2 do
p = p + 1
local ballRadius = 13 – radius of balls
redBall[p] = display.newCircle (redBallHoles[p].x, redBallHoles[p].y, ballRadius)
redBall[p]:setFillColor( 255, 0, 0)
redBall[p].value = p – this is the ball number (1 or 2)
redBall[p].currentHole = p – use to keep track of which hole the ball is in.
redBall[p].previousHole = p – use to keep track of the previous hole the ball was in
redBall[p].myName =“Red Ball”
redBall[p].hitCount = 0
redBall[p]:addEventListener( “touch”, onTouch )
local ballBody = {radius = ballRadius, filter = redBallCollisionFilter}
physics.addBody ( redBall[p], ballBody )
end

– display blue balls
local p = 0
for n = 1, 2 do
p = p + 1
local ballRadius = 13 – radius of balls
blueBall[p] = display.newCircle (blueBallHoles[p].x, blueBallHoles[p].y, ballRadius)
blueBall[p]:setFillColor( 0, 0, 255)
blueBall[p].value = p – this is the ball number (1 or 2)
blueBall[p].currentHole = p – use to keep track of which hole the ball is in.
blueBall[p].previousHole = p – use to keep track of the previous hole the ball was in
blueBall[p].myName =“Blue Ball”
blueBall[p].hitCount = 0
blueBall[p]:addEventListener( “touch”, onTouch )
local ballBody = {radius = ballRadius, filter = blueBallCollisionFilter}
physics.addBody ( blueBall[p], ballBody )
end
end

startNewGame()[/lua] [import]uid: 25787 topic_id: 7050 reply_id: 24757[/import]

Looks like the touch and collision listeners are not leaking. I stripped down my code to only include the four balls and the holes plus the touch and collision listeners. I have a leak somewhere else in my full code…off to figure that out.

Here’s what my memory usage looks like now.

73.78125 - at the beginning
86.5029296875 - after balls and holes are drawn
80.9248046875 - 1st ball is touched
81.1357421875 - 2nd ball is touched
81.1982421875 - 3rd ball is touched
81.3076171875 - 4th ball is touched
81.5185546875 - memory usage after 5th touch, it does not increase with subsequent touches
84.5947265625 - highest memory usage after all of the balls collide with all the holes

So the memory usage definitely stabilizes. I don’t quite understand why it drops after the balls and holes are drawn and then stabilizes. If anyone has any insight into the memory usage values we should expect or normal trends, I’d love to learn more.

[lua]local ballHolesCollisionFilter = { categoryBits = 1, maskBits = 6 }
local redBallCollisionFilter = { categoryBits = 2, maskBits = 1 }
local blueBallCollisionFilter = { categoryBits = 4, maskBits = 1 }

local function garbagePrinting()
collectgarbage(“collect”)
print(collectgarbage(“count”))
end
local function startNewGame ()
garbagePrinting()

local physics = require (“physics”)
physics.start()
physics.setGravity( 0, 0 ) – overhead view, therefore no gravity vector
–physics.setDrawMode( “debug” )

local collideTable = {nil}
local redBallHoles = {nil}
local blueBallHoles = {nil}
local redBall = {nil}
local blueBall = {nil}

local function onTouch( event )
local ball = event.target – this is the ball being touched
local phase = event.phase

if “began” == phase then
– Make target the top-most object
local parent = ball.parent
parent:insert( ball )
display.getCurrentStage():setFocus( ball )
ball.isFocus = true

garbagePrinting()

– Store ball’s initial position for moving purposes
ball.x0 = event.x - ball.x
ball.y0 = event.y - ball.y

elseif ball.isFocus then
if “moved” == phase then – “moved” means the ball is still being touched and moved
ball.x = event.x - ball.x0
ball.y = event.y - ball.y0

elseif “ended” == phase then – “ended” means the the ball is no longer being touched
display.getCurrentStage():setFocus( nil )
ball.isFocus = false
end

elseif “cancelled” == phase then
display.getCurrentStage():setFocus( nil )
ball.isFocus = false
end

return true
end

local function collision( self, event ) – Self is the hole and other is the ball. Event is the collision
garbagePrinting()

if event.phase == “began” then
elseif event.phase == “ended” then
end
end

– display board
local boardSize = { 920, 350 }
local boardX = (display.contentWidth - boardSize[1])/2 --x position of board’s top left corner
local boardY = (display.contentHeight - boardSize[2])/2 --y position of board’s top left corner
local board = display.newRoundedRect(boardX, boardY, boardSize[1], boardSize[2], 25) --draw cribbage board
board.strokeWidth = 5
board:setFillColor(0, 10, 10)
board:setStrokeColor(180, 180, 180)

– draw four starter holes
local ballHoleRadius = 10 --radius of ball hole
redBallHoles[1] = display.newCircle (boardX + 30, boardY + 70, ballHoleRadius)
redBallHoles[1].value = 0
redBallHoles[2] = display.newCircle (boardX + 30, boardY + 120, ballHoleRadius)
redBallHoles[2].value = 0
blueBallHoles[1] = display.newCircle (boardX + 30, boardY + 200, ballHoleRadius)
blueBallHoles[1].value = 0
blueBallHoles[2] = display.newCircle (boardX + 30, boardY + 250, ballHoleRadius)
blueBallHoles[2].value = 0
– draw red ball holes
local p = 2 – the first 2 holes in this array are for the starter holes
local j = 0
local f = 0
for h = 0, 2 do
for i = 0, 4 do
p = p +1
local ballHoleRadius = 10 --radius of ball hole
local redBallHoleX = 80 – ballhole x offset, 1st column
local redBallHoleY = 80 – ballhole y offset, 1st row
local x1 = boardX + redBallHoleX + 51*i + 260*h --51 is hole spacing in x (ballRadius*2 + ballHoleRadius*2 + 5)
local y1 = boardY + redBallHoleY + 55*j + 150*f --55 is hole spacing in y between rows 1 & 2 and 3 & 4
redBallHoles[p] = display.newCircle (x1, y1, ballHoleRadius)
redBallHoles[p]:setFillColor (255,255,255)

physics.addBody( redBallHoles[p], “static”, { radius=ballHoleRadius, isSensor=true, filter = ballHolesCollisionFilter } ) – make ballHoles physics body and sensor
redBallHoles[p].collision = collision
redBallHoles[p]:addEventListener( “collision”, redBallHoles[p]) – add collision table listener to each hole sensor
–redBallHoles[p].isVisible = true --set to false to hide. Make sure collision is still active if holes are hidden
–redBallHoles[p].myName = “Red Ball Holes”
end
end

– draw blue ball holes
local p = 2
local j = 0
local f = 0
for h = 0, 2 do
for i = 0, 4 do
p = p +1
local ballHoleRadius = 10 --radius of ball hole
local blueBallHoleX = 80 – ballhole x offset, 1st column
local blueBallHoleY = 130 – ballhole y offset, 1st row
local x1 = boardX + blueBallHoleX + 51*i + 260*h --51 is hole spacing in x (ballRadius*2 + ballHoleRadius*2 + 5)
local y1 = boardY + blueBallHoleY + 55*j + 150*f --55 is hole spacing in y between rows 1 & 2 and 3 & 4

blueBallHoles[p] = display.newCircle (x1, y1, ballHoleRadius)
blueBallHoles[p]:setFillColor (255,255,255)

physics.addBody( blueBallHoles[p], “static”, { radius=ballHoleRadius, isSensor=true, filter = ballHolesCollisionFilter } ) – make ballHoles physics body and sensor
blueBallHoles[p].collision = collision
blueBallHoles[p]:addEventListener( “collision”, blueBallHoles[p]) – add collision table listener to each hole sensor
–blueBallHoles[p].isVisible = true --set to false to hide. Make sure collision is still active with hidden holes
–blueBallHoles[p].myName = “Blue Ball Holes”
end
end

– display red balls
local p = 0
for n = 1, 2 do
p = p + 1
local ballRadius = 13 – radius of balls
redBall[p] = display.newCircle (redBallHoles[p].x, redBallHoles[p].y, ballRadius)
redBall[p]:setFillColor( 255, 0, 0)

redBall[p]:addEventListener( “touch”, onTouch )
local ballBody = {radius = ballRadius, filter = redBallCollisionFilter}
physics.addBody ( redBall[p], ballBody )
end

– display blue balls
local p = 0
for n = 1, 2 do
p = p + 1
local ballRadius = 13 – radius of balls
blueBall[p] = display.newCircle (blueBallHoles[p].x, blueBallHoles[p].y, ballRadius)
blueBall[p]:setFillColor( 0, 0, 255)

blueBall[p]:addEventListener( “touch”, onTouch )
local ballBody = {radius = ballRadius, filter = blueBallCollisionFilter}
physics.addBody ( blueBall[p], ballBody )
end

garbagePrinting()
end

startNewGame()[/lua]

[import]uid: 25787 topic_id: 7050 reply_id: 25025[/import]