Memory Leak, help, 3rd day and can't find [Solved]

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
BPLeon
Prole
Posts: 2
Joined: Tue Jan 27, 2015 4:20 pm

Memory Leak, help, 3rd day and can't find [Solved]

Post by BPLeon »

I usually never ask for help on this kind of stuff, it took me 3 days of debate whether to ask or not. I decided to try using Love2D to help myself understand lua a bit better, and while working with it, I somehow made myself a memory leak when making a clone of breakout , there isn't much code and I was relatively sure I set the references to nil, but I can't seem to find it.

Orz

To Produce Leak:
Lose all your lives 2-3 times and gameplay takes a huge toll in performance.
At the restart game function in game state I call self.gObjBrick = self:generateLevel() after removing everything from table :*(
It should be somewhere around there.

Please help :cry:
Attachments
BreakoutClone.love
My breakout Clone
(31.79 KiB) Downloaded 103 times
Last edited by BPLeon on Tue Jan 27, 2015 9:58 pm, edited 1 time in total.
Muris
Party member
Posts: 131
Joined: Fri May 23, 2014 9:18 am

Re: Memory Leak, help, 3rd day and can't find :*(

Post by Muris »

I havent used any physics engines for love2d so this is more like a guess, but from quick glance you aren't removing the collider from the world.

Code: Select all

function BRICK:Destroy()
    self.xPos = nil
    self.yPos = nil
    self.width = nil
    self.height = nil
    self.bColor = nil 
    Collider:remove( self.Collider )   -- <-- added this line, it removes the collision from the world at least I think it does
    self.Collider = nil
    self.drawnBrick = nil
    self = nil
end
Another problem/potential problem in future that I noticed in your code is inside the removing from table. You have inside gamestate, oncollide following thing:

Code: Select all

 for a, brick in ipairs (self.gObjBrick) do
...
            brick:Destroy()
            self.gObjBrick[a] = nil
            table.remove(self.gObjBrick,a)
...
end
Now this doesn't probably do exactly what you are hoping it does in case you have collision with 2 bricks at the same time. It skips objects, because when you remove object from an array with table.remove(), the size of table also gets substracted. Now lets say for example:

Code: Select all

local t = {
  1,2,3,4,5
}

for i,v in ipairs( t ) do
  if i == 3 or i == 4 then
    table.remove(t,i)
    print( i, v )
  end
end
Now you would think that this actually removes numbers 3 and 4, but it actually removes values 3 and 5, because the table is being changed while it is being iterated. If you index the table from max to min you can avoid this problem where you actually skip the indexes when removed (this is common problem with linked lists). For example once i hits 3, the table looks like { 1,2,4,5 }, and then 4th index is actually 5.


Edit:

I went outside for a walk and thought about this, and I came to conclusion that you are most likely, without really knowing how hardoncollider works, checking all the collisions against each other collisions. This means that essentially you are doing 99% of unneccessary collision checks, bricks vs brick collision. This leads to n*n amount of collision checks. The way how this is generally solved is by assigning collision groups, and with groups you only check each group against each group. Basically if using something like table logic what I am suspecting you are doing for collision check is something like:

Code: Select all

      | Ball | Brick1   | Brick2 | Brick3 | Brick4 | ... | Paddle |
------+------+----------+--------+--------+--------+-----+--------+
Ball  |      |     x    |    x   |   x    |    O   | ... |    x   |
Brick1|  x   |          |    x   |   x    |    X   | ... |    x   |
Brick2|  x   |     x    |        |   x    |    X   | ... |    x   |
Brick3|  x   |     x    |    x   |        |    X   | ... |    x   |
Brick4|  x   |     x    |    x   |   x    |        | ... |    x   |
...
Paddle|  x   |     x    |    x   |   x    |    X   | ... |        |
Well that is just my educated guess, someone with better knowledge about hardoncollider can correct me if I am wrong. Also I wouldn't be surprised if this doesn't make things any clearer.
User avatar
Azhukar
Party member
Posts: 478
Joined: Fri Oct 26, 2012 11:54 am

Re: Memory Leak, help, 3rd day and can't find :*(

Post by Azhukar »

The problem is with BRICK:Destroy() just like Muris wrote.

Most of your =nil, all of your garbagecollect and table.removes are useless. Especially this beauty:

Code: Select all

self=nil
BPLeon
Prole
Posts: 2
Joined: Tue Jan 27, 2015 4:20 pm

Re: Memory Leak, help, 3rd day and can't find :*(

Post by BPLeon »

Code: Select all

function BRICK:Destroy()
    self.xPos = nil
    self.yPos = nil
    self.width = nil
    self.height = nil
    self.bColor = nil
    Collider:remove( self.Collider )   -- <-- added this line, it removes the collision from the world at least I think it does
    self.Collider = nil
    self.drawnBrick = nil
    self = nil
end
This line was exactly what I needed, it actually removes the object and the performance stays the same.
Another problem/potential problem in future that I noticed in your code is inside the removing from table. You have inside gamestate, oncollide following thing:

Code: Select all

for a, brick in ipairs (self.gObjBrick) do
    ...
                brick:Destroy()
                self.gObjBrick[a] = nil
                table.remove(self.gObjBrick,a)
    ...
    end
Now this doesn't probably do exactly what you are hoping it does in case you have collision with 2 bricks at the same time. It skips objects, because when you remove object from an array with table.remove(), the size of table also gets substracted...
Are you talking about this part of it?

Code: Select all

            ballToBrick = self:Normalize2({NormX,NormY})
            angle = math.deg(math.acos(self:Dot(ballToBrick,{0,1})))+tempAngle
            brick:Destroy()
            self.gObjBrick[a] = nil
            table.remove(self.gObjBrick,a)
            --self.txtScore = self.txtScore + 10
            collectgarbage("collect")
            
            if angle <= 45 or angle >= 315 then
                self.gObjBall:goUp()
            elseif angle <= 135 and angle >= 45 then
                self.gObjBall:goRight()
            elseif angle <= 225 and angle >= 135 then
                self.gObjBall:goDown()
            elseif angle <= 315 and angle >= 225 then
                self.gObjBall:goLeft()
            end
            return
Doesn't the return at the end stop the loop and gets out of the function? It finds the brick we collided with, remove it and exits checks?
This means that essentially you are doing 99% of unneccessary collision checks, bricks vs brick collision. This leads to n*n amount of collision checks.

I do agree with this statement, I'm not really sure how hardoncollider works with something like this, and the fact that the collider still persist and the fact more getting added on makes the game lag n*n number of times, thus checking every single one and hitting

Code: Select all

    local other
    if shapea == self.gObjBall.Collider then
        other = shapeb
    elseif shapeb == self.gObjBall.Collider then
        other = shapea
    else
        return
    end
This all makes sense now, however, for now I have a temporary workaround to reduce the amount of checks the collision has. Each and every single brick will still call the function but atleast its a true and false kind of check, a tad expensive, it would only check if one of the objects is a ball otherwise return out of the function, I am going to go look up on how to use collision group, but for now my solution is solved. Thanks :ultraglee:
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot], Google [Bot] and 7 guests