GameMaker creating ds_list via script causes memory leak?

When I create as ds_list and store it in a local variable in a script which returns this value does this cause a memory leak?
example:
script: createMyList
Code:
var myList = ds_list_create();
return myList;

objectList
StepEvent
Code:
if(mouse_check_button_pressed(mb_left))
{
list = createmyList();
}
 

chamaeleon

Member
When I create as ds_list and store it in a local variable in a script which returns this value does this cause a memory leak?
example:
script: createMyList
var myList = ds_list_create();
return myList;

objectList
StepEvent
if(mouse_check_button_pressed(mb_left))
{
list = createmyList();
}
You have a memory leak because none of your code shows a ds_list_destroy().. Joking aside, no, you don't have a leak from the use of that local variable, as long as you have code somewhere whose responsibility it is to destroy it when no longer needed. Since you're unsure about this, let me reinforce the fact that the return value from the various data structure creation functions is simply an integer, with no inherent meaning whatsoever. The only meaning for it is derived from calling a data structure function with it as the reference to the data structure. There is no hidden meaning associated with the variable (be it local, global, or instance) just because it happened to be returned by any of the data structure create functions. In other words, when you call a data structure function it will assume the number you pass in references a data structure of the type the function works with. When you assign, return, perform mathematical calculations with it (unlikely), it's just a number.
 

FrostyCat

Redemption Seeker
It will cause a memory leak if the target receiving the returned value doesn't clean after it (in this case using ds_list_destroy()), or if it returned to nowhere. Your example would leak on every click past the first.
 
I'm sorry I am not quite shure if I understand this.

So, if I implement something like this
destroy event:
Code:
ds_list_destroy(list);
does every mouse click still create a memory leak because the script in which the ds_list gets created doesnt overwrite the current ds list?

so should I do something like this, too, in order to prevent memory leak creation?
step event of the objectList:
Code:
if(mouse_check_button_pressed(mb_left))
{
 if(ds_exists(list,ds_list))
 {
  ds_list_destroy(list);
 }
 list = createmyList();
}
sorry that the code is all in one height somehow the indent doesnt move the code to the right but just writes [ INDENT]
 

TsukaYuriko

☄️
Forum Staff
Moderator
Indentation in code blocks works based on spaces or tabs, not based on the INDENT BBcode.

A memory leak will occur if the amount of calls to ds_list_destroy is not equal to the amount of calls to ds_list_create over the lifetime of an instance of this object.

Either destroy or empty this list before it is recreated, if recreating it once in a while is necessary, or don't recreate it if it already exists.
 

FrostyCat

Redemption Seeker
So, if I implement something like this
destroy event:
Code:
ds_list_destroy(list);
does every mouse click still create a memory leak because the script in which the ds_list gets created doesnt overwrite the current ds list?
Two things:
  • Never use the Destroy event to clean after data structures or other ID-indexed resources. Changing rooms does not trigger the Destroy event, so you will leak the instant you go to another room. Always use the Cleanup event.
  • When you call list = createmyList(); repeatedly, all you are doing is pointing the same variable to a different list on each call. You are not overwriting the lists themselves or cleaning them up. You will still leak on every call past the first.
so should I do something like this, too, in order to prevent memory leak creation?
step event of the objectList:
Code:
if(mouse_check_button_pressed(mb_left))
{
    if(ds_exists(list,ds_list)
    {
        ds_list_destroy(list);
    }
    list = createmyList();
}
ds_exists() is not capable of forgiving references to undeclared variables. You still have to declare list ahead of time.

Create:
Code:
list = ds_list_create();
Step:
Code:
if (mouse_check_button_pressed(mb_left))
{
    ds_list_destroy(list);
    list = createmyList();
}
Cleanup:
Code:
ds_list_destroy(list);
Alternatively, change the script so that instead of creating a new list, it takes a list and clears and writes to it.
Code:
///@func writeMyList(list)
ds_list_clear(argument0);
/* Write to the list at argument0 here */
Create:
Code:
list = ds_list_create();
Step:
Code:
if (mouse_check_button_pressed(mb_left))
{
    writeMyList(list);
}
Cleanup:
Code:
ds_list_destroy(list);
 

NightFrost

Member
A ds list is basically a memory allocation. If you don't destroy the old list, it will still exist after you've created a new one, all you'd be doing was saving the pointer to the new list over the old to the list variable. It would be good to destroy the list as soon as its use is over, but we don't know how you've implemented your code. Another alternative would be to define the list in Create event and just repopulate it on mouseclick, but again we don't know your implementation and how well this might suit it.
 
Two things:
  • Never use the Destroy event to clean after data structures or other ID-indexed resources. Changing rooms does not trigger the Destroy event, so you will leak the instant you go to another room. Always use the Cleanup event.
  • When you call list = createmyList(); repeatedly, all you are doing is pointing the same variable to a different list on each call. You are not overwriting the lists themselves or cleaning them up. You will still leak on every call past the first.

ds_exists() is not capable of forgiving references to undeclared variables. You still have to declare list ahead of time.

Create:
Code:
list = ds_list_create();
Step:
Code:
if (mouse_check_button_pressed(mb_left))
{
    ds_list_destroy(list);
    list = createmyList();
}
Cleanup:
Code:
ds_list_destroy(list);
Alternatively, change the script so that instead of creating a new list, it takes a list and clears and writes to it.
Code:
///@func writeMyList(list)
ds_list_clear(argument0);
/* Write to the list at argument0 here */
Create:
Code:
list = ds_list_create();
Step:
Code:
if (mouse_check_button_pressed(mb_left))
{
    writeMyList(list);
}
Cleanup:
Code:
ds_list_destroy(list);
Thank you very much. I will rewrite my code and then set this thread to solved. Hopefully this will fix the memory leak.

However, there is another bug which might be related to this memory leak. I am not shure if this ok to post it into this thread but I think this might be somehow related. If not I will create a new thread.


In my current project I run into a really weird error. I run into this issue when reading out data from a ds_list. Suddenly the value is not a number anymore but a string value. A string value which I actually have stored in a different anmed ds_list! Does this maybe relate to the memory leak? That maybe that because so many ds_list get created and not cleared/deleted that the pointer gets somehow confused or something?
 

TsukaYuriko

☄️
Forum Staff
Moderator
Data structure handles are numbers, not pointers. They don't point to memory, but to a position in a list of data structures of the respective types.

If any data structure is destroyed, its ID gets re-used when new data structures are created. A lack of proper data structure reference management - especially when referring to the same data structure from multiple places - can therefore lead to two inherently different variables pointing to the same data structure.
 

TheouAegis

Member
To clarify, suppose you have

mainlist=ds_list_create();
otherlist=ds_list_create();

So mainlist has a value of 0 and otherlist has a value of 1. If you destroy otherlist and create a new one, the new value of otherlist will be 2.

Then let's say somewhere in another object you create a new variable (let's call it newlist) and set it to a default value of 0. Even though you havenot yet assigned a list to newlist, it still points to a valid list -- list 0, which is mainlist.

Another possibility is if you accidentally set otherlist to a value of 0 directly. Then otherlist will be pointing to the same list as mainlist.
 
First of all thank you very much for your help!
Here is a little update just in cases if somebody runs into a similar problem and reads this thread:
I have not mentioned it before but I used a json file for the data and used this code
objectData:
Code:
myData = return json_decode(@'
{
   "player" : {
        "name"            : "Player",
        "hp"        : 2
    },
    "enemy" : {
        "name"          : "Enemy",
        "hp"        : 2
    }
}');
As I understood myData is a ds_map and contains two ds_list.
Apparently when I copy a ds_list out of the ds_map myData to a new variable and delete it I also delete the ds_list into the myData ds_map. One can see this using the debugger and then looking for the value assigned to them.
This trick can be used to avoid creating a reference but create a new copy of the ds_list. (Btw I didnt came up with but I saw it in a Udemy Tutorial for GM 1.4 by Benjamin Anderson)
objectPlayer:
Code:
var playerData = json_decode(json_encode(oData.myData[? "player"]));
playerName = playerData[? "name"];
playerHP = playerData[? "hp"];
ds_map_destroy(playerData);
 
Two things:
  • Never use the Destroy event to clean after data structures or other ID-indexed resources. Changing rooms does not trigger the Destroy event, so you will leak the instant you go to another room. Always use the Cleanup event.
game_restart() does not trigger the Destroy event, too?

What exactly does the cleanup event? When or what triggers it? (couldnt find it in the manual via search)
 

FrostyCat

Redemption Seeker
game_restart() does not trigger the Destroy event, too?

What exactly does the cleanup event? When or what triggers it? (couldnt find it in the manual via search)
Only instance_destroy() and position_destroy() trigger the Destroy event. Everything that can cause an instance to stop existing, including room changes, game restarts and of course instance_destroy() and position_destroy(), will trigger the Cleanup event.
 
Top