Pages: 1 2 »
  Print  
Author Topic: Find the Memory Leak  (Read 4246 times)
Offline (Male) Goombert
Posted on: March 30, 2014, 05:43:26 PM

Developer
Location: Cappuccino, CA
Joined: Jan 2013
Posts: 3110

View Profile
I am doing some debugging on object inheritance and noticed a memory leak in Mark Overmars original FPS example, this is only 1 of 3 bugs left in this game. The memory leak does not occur if you delete the collision event of obj_monster_basic. I am posting this here so maybe Josh or TheExDeus can spot the bug.

The obj_monster1 code parses out to this.
http://pastie.org/8981241

The obj_monster_basic code parses out to this.
http://pastie.org/8981237

This is the IDE_EDIT_objectfunctionality.h requested by Harri.
http://pastie.org/8984313
« Last Edit: March 31, 2014, 09:21:41 PM by Robert B Colton » Logged
I think it was Leonardo da Vinci who once said something along the lines of "If you build the robots, they will make games." or something to that effect.

Offline (Unknown gender) TheExDeus
Reply #1 Posted on: March 31, 2014, 09:58:06 AM

Developer
Joined: Apr 2008
Posts: 1872

View Profile
Post also the IDE_EDIT_objectfunctionality.h. You posted only the declarations, but the collision event code itself is not there. Unless you are implying the memory leak is even if the collision event is empty?
And how does the leak work? The memory keeps rising even when nothing is happening or just doesn't go down when you destroy the "obj_monster_basic" object?
Logged
Offline (Male) Goombert
Reply #2 Posted on: March 31, 2014, 10:45:17 AM

Developer
Location: Cappuccino, CA
Joined: Jan 2013
Posts: 3110

View Profile
Yes I am implying that Harri, I already replaced the collision event code with // do nothing and the memory leak still occurs. You have to fully delete and remove the collision event for the memory leak to stop. And the memory keeps on going up, about 1mb per second on the FPS example.
Logged
I think it was Leonardo da Vinci who once said something along the lines of "If you build the robots, they will make games." or something to that effect.

Offline (Unknown gender) TheExDeus
Reply #3 Posted on: March 31, 2014, 02:53:47 PM

Developer
Joined: Apr 2008
Posts: 1872

View Profile
Still, post both files.
And the one object in which collision detection leaks is a parent or a child of a parent? Or just an individual object?
Post the gmk. It will probably easier to figure it out. Sadly there are no very easy or good memory leak detectors in Windows. On Linux we have Valgrind which people usually praise.
Logged
Offline (Male) Goombert
Reply #4 Posted on: March 31, 2014, 09:19:29 PM

Developer
Location: Cappuccino, CA
Joined: Jan 2013
Posts: 3110

View Profile
obj_wall_basic
obj_wall1: obj_wallbasic
obj_monster_basic
obj_monster1: obj_monster_basic

Is the basic inheritance structure, the collision event belongs to obj_monster_basic and is checking for a collision with obj_wall_basic.

I have edited the original post with the parse output of the file you have requested.

The GMK can be downloaded anywhere, for instance, from here.
http://games.software4me.net/rpg/dload/index.php?cid=33
"    GM Tutorial - First Person Shooter - Mark Overmars - 12-April-2013 1:26 pm - 2.57 MB - 56"

I'd really like to get this fixed, but I've run out of ideas what could be causing it :(

Quote from: TheExDeus
On Linux we have Valgrind which people usually praise.
Also, I thought you said you use Windows? I guess you dual boot then don't you?
Logged
I think it was Leonardo da Vinci who once said something along the lines of "If you build the robots, they will make games." or something to that effect.

Offline (Unknown gender) TheExDeus
Reply #5 Posted on: April 01, 2014, 12:01:42 PM

Developer
Joined: Apr 2008
Posts: 1872

View Profile
After about 15min of debugging I tracked the problem to collide_inst_inst() [Collision_Systems/Precise/coll_impl.cpp:295] and subsequently to enigma::iterator::iterator() [Universal_System/instance_system.cpp:82]. It seems the problem is that the iterator is not released because the function returns a pointer (enigma::object_collisions*) and it is never deleted. The problem itself I guess manifests in the fact that collide_inst_inst() is ENIGMA namespace function only used by generated collision events. In IDE_EDIT_objectfunctionality.h we have this generated for that collision event:
Code: [Select]
variant enigma::OBJ_obj_monster_basic::myevent_collision_0()
{
  if (!((instance_other = enigma::place_meeting_inst(x,y,0)))) return 0;
for (enigma::iterator it = enigma::fetch_inst_iter_by_int(0); it; ++it) {int $$$internal$$$ = 0; instance_other = *it; if (enigma::place_meeting_inst(x,y,instance_other->id)) {if(enigma::glaccess(int(other))->solid && enigma::place_meeting_inst(x,y,instance_other->id)) x = xprevious, y = yprevious;

  {
   
    {
      x = xprevious;
      y = yprevious;
      if((abs(hspeed)>= abs(vspeed)&& not place_meeting(x + hspeed, y, obj_wall_basic)))
      {
        x += hspeed;
        return 0;
        ;
       
      }
      if(not place_meeting(x, y + vspeed, obj_wall_basic))
      {
        y += vspeed;
        return 0;
        ;
       
      }
      if(not place_meeting(x + hspeed, y, obj_wall_basic))
      {
        x += hspeed;
        return 0;
        ;
       
      }
     
    }
   
  }
  ;
  if (enigma::glaccess(int(other))->solid) {x += hspeed; y += vspeed; if (enigma::place_meeting_inst(x, y, $$$internal$$$)) {x = xprevious; y = yprevious;}}}}

  return 0;
}
I think the problem here is the use of pointers to iterators. That means C++ doesn't get rid of them (they don't destruct when going out of scope) and so they are never destroyed. This line "instance_other = *it;" just keeps making more of them. I at least think that is the problem. Josh maybe has better insight as he made the system. I personally think pointers are very ISO99 and we should use smart pointers or no pointers at all.

Quote
Also, I thought you said you use Windows? I guess you dual boot then don't you?
I do use only Windows on my laptop and main PC. But I do use Linux when programming on RaspberryPi.

edit: To see that the problem is somehow there I suggest commenting out for(){} in collide_inst_inst() function in coll_impl.cpp. Then the leak is gone.

Also, there seem to be a lot more leaks in ENIGMA, like in sprite structures which are never released. I used DrMemory to find leaks. Recommend trying it from here: http://www.drmemory.org/ .
« Last Edit: April 01, 2014, 12:04:51 PM by TheExDeus » Logged
Offline (Male) Josh @ Dreamland
Reply #6 Posted on: April 04, 2014, 11:31:48 AM

Prince of all Goldfish
Developer
Location: Pittsburgh, PA, USA
Joined: Feb 2008
Posts: 2958

View Profile Email
Robert, just remove blocks from the GML until the leak no longer occurs, then post the GML that can't be removed without fixing the leak. The leak will be in the code for those GML function(s).

My guess would be that in the best case, someone used new for no reason. In the worst case, it's creating some kind of resource that ENIGMA can never garbage collect/preempt.

polygone: Don't use an if-else ladder for event_perform, and don't call the parent's event first. If needed, the child event should either be the parent event, or should call it explicitly.
Code: (C++) [Select]
switch (type | numb << 8) { // Assumes type ≤ 255
  case (0 | 0 << 8): return myevent_create();
  case (1 | 0 << 8): return myevent_destroy();
  default: return 0;
}
Logged
"That is the single most cryptic piece of code I have ever seen." -Master PobbleWobble
"I disapprove of what you say, but I will defend to the death your right to say it." -Evelyn Beatrice Hall, Friends of Voltaire
Offline (Male) Goombert
Reply #7 Posted on: April 04, 2014, 12:15:43 PM

Developer
Location: Cappuccino, CA
Joined: Jan 2013
Posts: 3110

View Profile
Josh, I removed all GML code in the event, which is the following.
Code: (EDL) [Select]
{
  x = xprevious;
  y = yprevious;
  if (abs(hspeed) >= abs(vspeed) && not place_meeting(x+hspeed,y,obj_wall_basic))
    { x += hspeed; exit;}
  if not place_meeting(x,y+vspeed,obj_wall_basic)
    { y += vspeed; exit;}
  if not place_meeting(x+hspeed,y,obj_wall_basic)
    { x += hspeed; exit;}
}
I then replaced it with the following.
Code: (EDL) [Select]
// do nothing
And the leak still occurs, it only goes away if you delete the event from the parent object. Read Harri's report, you'll have a better understanding.

Edit: Upon further investigating Harri's claims which I have found to be true. I discovered exactly where and when the error occurs, this was tested with the BBox system. Simply removing line 100, the following code, fixes the memory leak.
Code: (C) [Select]
        if (left1 <= right2 && left2 <= right1 && top1 <= bottom2 && top2 <= bottom1)
            return inst2;

The question now is why this occurs with parenting and not without parenting. It could be related to the object index which is passed up to the parent in the constructor which is further passed to the object_locals base class. The current system does not allow iterating children of a parent object using the parent object index, I do not know if that is possible in GM or not.
« Last Edit: April 04, 2014, 12:45:04 PM by Robert B Colton » Logged
I think it was Leonardo da Vinci who once said something along the lines of "If you build the robots, they will make games." or something to that effect.

Offline (Male) Josh @ Dreamland
Reply #8 Posted on: April 04, 2014, 03:34:13 PM

Prince of all Goldfish
Developer
Location: Pittsburgh, PA, USA
Joined: Feb 2008
Posts: 2958

View Profile Email
Quote
The question now is why this occurs with parenting and not without parenting.
Make sure instantiating the parent enemy *does not* cause this problem, and instantiating an *empty* child enemy does.

Quote
It could be related to the object index which is passed up to the parent in the constructor which is further passed to the object_locals base class.
Quote
The only way that this could be at all relevant is if an iterator isn't getting destroyed properly. But the iterator system I set up relies on RAII principles, so that shouldn't be the case.

The current system does not allow iterating children of a parent object using the parent object index, I do not know if that is possible in GM or not.
It absolutely does. That feature is older than your event inheritance fix. If that is no longer the case, then my guess is that you broke it when you recoded the constructors for your event inheritance.

I don't see the problem from the code you've posted. I'd like the exact EDL for both events, ideally. From what I can see here, there's no difference between the two objects at all, since it's the parent's own function that is called, exactly. Valgrind could tell me the exact allocation position of the leaked objects, but I am away from my ENIGMA machine, at work. Valgrind would be infinitely more useful if we had a Graphics_Systems/None to avoid OpenGL errors from displaying in the backlog.
Logged
"That is the single most cryptic piece of code I have ever seen." -Master PobbleWobble
"I disapprove of what you say, but I will defend to the death your right to say it." -Evelyn Beatrice Hall, Friends of Voltaire
Offline (Male) Goombert
Reply #9 Posted on: April 06, 2014, 11:37:11 AM

Developer
Location: Cappuccino, CA
Joined: Jan 2013
Posts: 3110

View Profile
Ok, well I changed obj_monster_basic collision event to a collision with obj_wall1 instead of obj_wall_basic, and that also stopped the memory leak. Now I went further and made a test case that shows the bug also occurs without inheritance, just with collisions between objects.
By simply setting obj_monster1's sprite to <no sprite> the memory leak will also disappear, give it back it's sprite, and the memory leak comes back.

As for the instantiation of obj_monster_basic, I know that is not happening because a show_message call in the create event yields nothing. And I do not see them in the room instance list either, obj_wall_basic as well.

Now, I know there are 367 instances of objects that inherit obj_wall_basic, and 50 instances of objects that inherit obj_monster basic, because of the following code in the Room Create code.
Code: (EDL) [Select]
show_message(string(instance_number(obj_wall_basic)));
show_message(string(instance_number(obj_monster_basic)));
Which I also tried the following variation.
Code: (EDL) [Select]
show_message(string(instance_number(obj_wall_basic)));
show_message(string(instance_number(obj_monster1)));
Which yielded the same results.

Quote from: JoshDreamland
I'd like the exact EDL for both events, ideally
There's only 1 event, and I have provided the code for it. obj_monster_basic has a collision event with obj_wall_basic, obj_monster1 has no collision code and automatically inherits the collision event with obj_wall_basic

I have created the following minimal test cases that reproduce the bug.
WARNING! Run with caution, they leak 4mb's per second for me.

This is with inheritance.
https://www.dropbox.com/s/moe88bjkebm6ioi/inheritanceleak.gm81
This is one that has no inheritance, just a collision event between one object and another, the memory leak still occurs.
https://www.dropbox.com/s/4ac5evbozjfvczq/collisionmemleak.gm81
Logged
I think it was Leonardo da Vinci who once said something along the lines of "If you build the robots, they will make games." or something to that effect.

Offline (Male) cheeseboy
Reply #10 Posted on: April 06, 2014, 05:28:57 PM

Member
Location: The internet
Joined: Mar 2011
Posts: 106

View Profile
enigma just fulla leaks. shit leaks on empty gaym too
http://pastie.org/8999156
Logged
Offline (Male) Josh @ Dreamland
Reply #11 Posted on: April 12, 2014, 07:53:25 AM

Prince of all Goldfish
Developer
Location: Pittsburgh, PA, USA
Joined: Feb 2008
Posts: 2958

View Profile Email
Narrow as small a test case as you can. That's always the rule. I can't just hope the leak occurs on my machine and trace out a likely cause throughout a whole game.

And no one cares about reachable blocks, cheesedip. Someone should probably fix the leak in the sprite loader, but that only runs once. You're better off finding an actual, repeated leak.
Logged
"That is the single most cryptic piece of code I have ever seen." -Master PobbleWobble
"I disapprove of what you say, but I will defend to the death your right to say it." -Evelyn Beatrice Hall, Friends of Voltaire
Offline (Unknown gender) Darkstar2
Reply #12 Posted on: April 12, 2014, 10:59:06 AM
Member
Joined: Jan 2014
Posts: 1244

View Profile Email
enigma just fulla leaks. shit leaks on empty gaym too
http://pastie.org/8999156

Strange but I noticed it it too with an empty project, no objects, nothing, just an empty room. :P
Logged
Offline (Male) Goombert
Reply #13 Posted on: September 26, 2014, 12:07:38 AM

Developer
Location: Cappuccino, CA
Joined: Jan 2013
Posts: 3110

View Profile
Just an update me and Josh got a patch for the instance iterator fix that stops these massive memory leaks, Mark Overmars FPS example runs now at 15.2 MB's and stays there, 5 MB's lower than a blank GM8.1 game and minimal leak if any though I am sure there is some minor leakage from other systems, just by the design of GM.
https://github.com/enigma-dev/enigma-dev/pull/835
« Last Edit: September 26, 2014, 12:09:32 AM by Robert B Colton » Logged
I think it was Leonardo da Vinci who once said something along the lines of "If you build the robots, they will make games." or something to that effect.

Offline (Unknown gender) lonewolff
Reply #14 Posted on: September 26, 2014, 12:24:06 AM
"Guest"


Email
Hang on a sec. How can the design of GM cause a memory leak in ENIGMA?

GM is guilty of a lot of things but memory leaks in a rival product is probably not one of those things :)
Logged
Pages: 1 2 »
  Print