• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!
  • Hello [name]! Thanks for joining the GMC. Before making any posts in the Tech Support forum, can we suggest you read the forum rules? These are simple guidelines that we ask you to follow so that you can get the best help possible for your issue.

Diagnosing Memory Leaks?

S

Soulie

Guest
Hi again all,

I've been struggling with a memory leak the last few months.
Is there a method of seeing what lines of code are causing the leak?



I have a ds_list of trees called 'treelist' in my endless game that are added to the list at the right of the screen and deleted when they move past the left side.

in the Room End event I've written: ds_list_destroy(treelist); which frees ds_list from memory (I hope!)

I tried going through each line with the debugger to see when the memory usage increased. It does seem to only happen when the trees are scrolling across the screen and so here is the relevant code:

Code:
if (num_trees > 0)
{
    //Copy tree 0's 2D array
    var temp_array = treelist[| 0];
    // if the x-coordinate is to the left of the view
    if (temp_array[0,0] < view_xview[0] - 0.2 * view_wview[0])
    {
        ds_list_delete(treelist, 0);
        num_trees = ds_list_size(treelist);
        temp_array = treelist[| 0];
    }
    // Tree and Branches movement
    var i_tree = 0
    // Iterate through trees
    do
    {
        var temp_array = treelist[| i_tree];
        // move the current x co-ord of tree
        temp_array[0,0] -= screen_move
        // get the num of branches of this tree
        var branches = array_height_2d(temp_array);
        for(var j = 1 ; j < branches; j += 1;)
        {
            temp_array[j, 0] -= screen_move;
            temp_array[j, 2] -= screen_move;
        }
        // copy the tree's modified array back to the ds_list
        treelist[| i_tree] = temp_array;
        i_tree++;
    }
    until (i_tree >= num_trees);
}
I have a feeling that after a ds_list array has been copied to the local variable temp_array, the memory for temp_array is not getting freed. Is this possible?

Any suggestions are welcome, really! It's been troubling me too long haha!
(I hope it's just a simple mistake that I've made)

Maybe there's a good method to troubleshoot memory leaks in GM?

Many, many thanks guys,
 
S

Soulie

Guest
I cleaned up the code a bit (was making multiple temp_array variables)
But I'm still getting the memory leak

Code:
if (num_trees > 0)
{
    //Copy tree 0's 2D array
    var temp_array = treelist[| 0];
    // if the x-coordinate is to the left of the view
    if (temp_array[0,0] < view_xview[0] - 0.2 * view_wview[0])
    {
        ds_list_delete(treelist, 0);
        num_trees = ds_list_size(treelist);
    }
    // Tree and Branches movement
    var i_tree = 0
    // Iterate through trees
    do
    {
        temp_array = treelist[| i_tree];
        // move the current x co-ord of tree
        temp_array[0,0] -= screen_move
        // get the num of branches of this tree
        var branches = array_height_2d(temp_array);
        for(var j = 1 ; j < branches; j += 1;)
        {
            temp_array[j, 0] -= screen_move;
            temp_array[j, 2] -= screen_move;
        }
        // copy the tree's modified array back to the ds_list
        treelist[| i_tree] = temp_array;
        i_tree++;
    }
    until (i_tree >= num_trees);
}
 
S

Shihaisha

Guest
If you are sure that the leak is caused by that tree generating code, just get rid of it. That's what I would do, especially if it was giving me a headache for months.

Once I was doing a level with endlessly scrolling objects, and I just set an alarm (which can have a fixed or irandom_range interval), it created an object (tree), and then began a countdown for next object. To loop through several kinds of trees you can set a variable, which can be increased by the same alarm, and create a new tree depending on the value of that variable. Maybe that solution isn't as elegant as all those arrays and lists, but at least you'll know for sure what is going on in your code and that there is no memory leak.
 
S

Soulie

Guest
If you are sure that the leak is caused by that tree generating code, just get rid of it. That's what I would do, especially if it was giving me a headache for months.

Once I was doing a level with endlessly scrolling objects, and I just set an alarm (which can have a fixed or irandom_range interval), it created an object (tree), and then began a countdown for next object. To loop through several kinds of trees you can set a variable, which can be increased by the same alarm, and create a new tree depending on the value of that variable. Maybe that solution isn't as elegant as all those arrays and lists, but at least you'll know for sure what is going on in your code and that there is no memory leak.
Thanks Shihaisha for the helpful reply.

It is definitely a bit more complex with all these ds_lists and arrays, but the reason for that is to avoid the performance hit of having the scrolling trees as objects. I'm trying to optimize the game as much as possible because it's for mobile.

Tbh I've been trying to see when the memory usage jumps up after each line of code, but I don't think this is the right way to go about it.
 
S

Shihaisha

Guest
You don't necessarily have to create all trees as objects. It could be one tree controller object with several alarms, and each of those alarms counting down a variable for x position of a tree. As soon as that variable reaches, e.g., -200 (which will mean the tree has moved outside of view/room), rewind it to 1000 (new x position in the right part of the screen) and begin counting down again. The tree controller will draw tree sprites with all those x variables constantly moving to the left.

My suggestion basically was, that if some complex code gives you a trouble and takes a lot of time to debug, you could just replace it with something more simple and move on.
 
J

Jaqueta

Guest
Sometimes when I got a bug or something like that, I put a chunk of code inside a /* comment */
I keep doing that till I figure which chunk of code is the cause of the problem, and then inside that chunk, I do the same with smaller parts of code.

It takes some time to find the problem, but it's incredible effective.

Also, try to look into other things that may cause this leak, External files, Surfaces, Resources created with code (Like Sprite add and stuff)
 
S

Soulie

Guest
You don't necessarily have to create all trees as objects. It could be one tree controller object with several alarms, and each of those alarms counting down a variable for x position of a tree. As soon as that variable reaches, e.g., -200 (which will mean the tree has moved outside of view/room), rewind it to 1000 (new x position in the right part of the screen) and begin counting down again. The tree controller will draw tree sprites with all those x variables constantly moving to the left.

My suggestion basically was, that if some complex code gives you a trouble and takes a lot of time to debug, you could just replace it with something more simple and move on.
I see your point Shihaisha and it's a great idea. It would be nice to start over with a different and simpler method, but the trees have a lot of things going on with them: Bugs, lemurs, branches on either side etc...
That may mean I'd have to re-write A LOT of code.

Here's what I mean

Sometimes when I got a bug or something like that, I put a chunk of code inside a /* comment */
I keep doing that till I figure which chunk of code is the cause of the problem, and then inside that chunk, I do the same with smaller parts of code.

It takes some time to find the problem, but it's incredible effective.

Also, try to look into other things that may cause this leak, External files, Surfaces, Resources created with code (Like Sprite add and stuff)
Thank you, Jaqueta. I'm gonna do exactly that now!


Thanks guys, I'll see if I can hunt down the problem code!
 
I

icuurd12b42

Guest
this code can be problematic
temp_array[j, 0] -= screen_move;

use the accessor to prevent studio from duplicating the array and overfloading the garbage collector with dead array clones

not sure the syntax is right for 2d arrays
temp_array[@j, 0] -= screen_move;

you dont need to do this!
temp_array = treelist[| i_tree];
then
treelist[| i_tree]; = temp_array;
if you use the accessor. you only need to do this because you did temp_array[j, 0] -= screen_move; which dereferences the array and created a clone...

It's a little nutty but If you can find the to_array script on the old forum this code should make things clearer

//assign an array to each item in list 1
l = ds_list_create()
for(i = 0; i<100; i++)
{
l[|i] = to_array(1,2,3,4,5);
}

// a second list (list 2) holding a reference to the same arrays
l2 = ds_list_create()
for(i = 0; i<100; i++)
{
l2[|i] = l[|i];
}

//add to element 0 of all the arrays in the list
for(i = 0; i<100; i++)
{
arr = l[|i];
arr[@0]++;
}


//Sanity check list 1 vs list 2
for(i = 0; i<100; i++)
{
arr = l[|i];
show_debug_message(arr);
arr2 = l2[|i];
show_debug_message(arr2);
}

This should give you less leakage.

you see I dont need to set l[|i] back to arr... because I changed the element [@0] directly. and everything that holds a reference to the array is still referencing the same array instance and so everyone is accessing the same data. if you dont use the accessor, and use arr[0]++, then the array is cloned and arr would not be the same array as in list 1 and list 2... the references is lost. List 1 and list 2 would point to the same original arrays, arr would be a copy and then you l[|i] = arr... arr and l[!i] are the same array but now list 2 has it's own copy. The 2 lists dont point to the same data.

Now I can deref the array arr directly to sort of force the reference count back as opposed to let the garbage collector decide if the element is dead

//Adding to the end of the sanity check list 1 vs list 2
for(i = 0; i<100; i++)
{
arr = l[|i];
show_debug_message(arr);
arr2 = l2[|i];
show_debug_message(arr2);
}
arr = 0; //<< here
arr2 = 0; //<< here

or if I have an array in an array, I can set the element to 0
arr[@0] = to_array(1,2,3,4,5);
arr[@1] = to_array(1,2,3,4,5);
I should deref the element
arr[@0] = 0;
arr[@1] = 0;
before dereferencing / freeing the array itself
arr = 0;
or if I have an array in a list, like you do, you can set the l[!i] = 0 before removing the item from the list

The manual dereferencing is a bit extreme but if you find you dont get back your memory fast enough or ever you should probably consider it.
 
Last edited by a moderator:
S

Soulie

Guest
icuurd can I marry you?

This just fixed the whole memory leak!

this code can be problematic
temp_array[j, 0] -= screen_move;

use the accessor to prevent studio from duplicating the array and overfloading the garbage collector with dead array clones

not sure the syntax is right for 2d arrays
temp_array[@j, 0] -= screen_move;
Memory Leak Free code:

Code:
    do
    {
        temp_array = treelist[| i_tree];
        // move the current x co-ord of tree
        temp_array[@ 0,0] -= screen_move
        // get the num of branches of this tree
        branches = array_height_2d(temp_array);
        for(var j = 1 ; j < branches; j += 1;)
        {
            temp_array[@ j, 0] -= screen_move;
            temp_array[@ j, 2] -= screen_move;
        }
        i_tree++;
        temp_array = 0
    }
    until (i_tree >= num_trees);
So my problem was I had a ds_list of 2D arrays, which you can't directly modify as far as I know.
So I thought temp_array = treelist[| i_tree]; copies the 2D array from the ds_list but it actually serves as a pointer to it.

temp_array[ j, 0] -= screen_move would make a copy of the 2D array and then modify it
temp_array[@ j, 0] -= screen_move on the other hand dereferences the array and changes the value within the ds_list itself.

I hope I got that right.

Thank you all for the help, this brings me so much relief! :)
 
Top