GML What is causing memory leak?

emicarra

Member
Hello, i made a simple server for my online game, and the task manager is showing it is slowly increasing memory usage, when i open the server the memory usage is about 18.000kb and after around 10 hours, the server reaches 200.000kb or more. There's always around 40 players online.

All the server program does, is send and receive data using buffers, i create and delete them instantly to send data, and according to the gms2 manual, the networking event automatically creates one buffer to access the received data and then automatically deletes it.
The server has 2 ds_lists and 1 ds_map to store some data from joining players, and when the player leaves, the data from that player is deleted using ds_list_delete and ds_map_delete.

I don't understand what can be causing the memory leak, since that's all the server does and as you can see i made sure to delete data from ds lists, maps, and buffers after no longer needed.
My only guess is that game maker is probably not deleting the buffer data after the server receives data from players in the networking event. Or am i doing something wrong? Thanks for your help
 
ds_list/map_delete only remove the information in a data structure. Are you sure you wanted those and not ds_list/map_destroy, which destroy the data structure and free memory?
 

emicarra

Member
ds_list/map_delete only remove the information in a data structure. Are you sure you wanted those and not ds_list/map_destroy, which destroy the data structure and free memory?
Thanks for your response, i'm storing the data of all the players inside the ds_list, so i remove the data of specific players from the ds_list when they leave, if i delete the ds_list itself i would lose the data of all players, i assume when you delete data from the ds_list it should free the memory used. Thanks anyways
 

FrostyCat

Redemption Seeker
If you nest lists inside maps or maps inside lists, make sure to use the appropriate marking function:
  • ds_map_add_map()
  • ds_map_add_list()
  • ds_list_mark_as_map()
  • ds_list_mark_as_list().
If you don't, simply deleting entries won't free the nested content.
 

emicarra

Member
If you nest lists inside maps or maps inside lists, make sure to use the appropriate marking function:
  • ds_map_add_map()
  • ds_map_add_list()
  • ds_list_mark_as_map()
  • ds_list_mark_as_list().
If you don't, simply deleting entries won't free the nested content.
Thanks for your response, i only store strings and integers inside the maps.
Also i just tested if the ds lists or maps were causing the leak by destroying them, but after destroying them the memory usage was the same.
I also tested to delete the buffer created by the async networking event after it was used in case game maker didnt delete it automatically, but that didnt change anything.

So, this doesn't make any sense, i'm not doing anything that would cause a memory leak, I'll post my entire code here so you can see:

Server Create event:
Code:
iplist = ds_list_create();
portlist = ds_list_create();
playersmap = ds_map_create();
socket = network_create_socket_ext(network_socket_udp,8308);
alarm[2] = 1; //Set reliable udp

alarm[0] = room_speed*10;

gameversion = 51;
bosslvl = 1;
bossdef1 = "";
bossdef2 = "";
bossdef3 = "";
Server Alarm event:
Code:
//Ping players, if there's no response kick them
alarm[0] = room_speed*10;

for(var f=0;f<ds_list_size(iplist);f+=1){
    var ip = ds_list_find_value(iplist,f);
    var port = ds_list_find_value(portlist,f);
    if(ds_map_find_value(playersmap,"afk"+string(ip)+string(port)) != 1){
        ds_map_replace(playersmap,"afk"+string(ip)+string(port),1);
        var buff = buffer_create(1,buffer_grow,1);
        buffer_seek(buff,buffer_seek_start,0);
        buffer_write(buff,buffer_u8,2);
        network_send_udp(socket, ip, port, buff, buffer_tell(buff));
        buffer_delete(buff);
    }else{
        ds_list_delete(iplist,f);
        ds_list_delete(portlist,f);
        ds_map_delete(playersmap,"afk"+string(ip)+string(port));
    }
}
Server Draw event:
Code:
col = c_white;
if(socket >= 0){
draw_text_color(x,y,"Server ON",col,col,col,col,1);
}
draw_text_color(x,y+16,string(ds_list_size(iplist))+" plrs",col,col,col,col,1);
draw_text_color(x,y+32,"Boss lvl "+string(bosslvl),col,col,col,col,1);
Server Networking event:
Code:
if(async_load[?"type"] == network_type_data){
    var tbuff =  async_load[?"buffer"];
    var ip = async_load[?"ip"];
    var port = async_load[?"port"];
    
    var plrindex = -1;
    for(var f=0;f<ds_list_size(iplist);f+=1){
        if(ds_list_find_value(iplist,f) == ip && ds_list_find_value(portlist,f) == port){
            plrindex = f;
        }
    }
    var cmd = buffer_read(tbuff,buffer_u8);
    if(plrindex == -1 && cmd != 1){ //Message from player that didnt connect yet
        exit;   
    }
    switch(cmd){
        case 1: //New connection
            ds_list_add(iplist,ip);
            ds_list_add(portlist,port);
            ds_map_add(playersmap,"afk"+string(ip)+string(port),0);
            //Send game version
            var buff = buffer_create(2,buffer_grow,1);
            buffer_seek(buff,buffer_seek_start,0);
            buffer_write(buff,buffer_u8,1);
            buffer_write(buff,buffer_u8,gameversion);
            network_send_udp(socket, ip, port, buff, buffer_tell(buff));
            buffer_delete(buff);
            //Send boss stats
            var buff = buffer_create(128,buffer_grow,1);
            buffer_seek(buff,buffer_seek_start,0);
            buffer_write(buff,buffer_u8,3);
            buffer_write(buff,buffer_u8,bosslvl);
            buffer_write(buff,buffer_string,bossdef1);
            buffer_write(buff,buffer_string,bossdef2);
            buffer_write(buff,buffer_string,bossdef3);
            network_send_udp(socket, ip, port, buff, buffer_tell(buff));
            buffer_delete(buff);
        break;
        case 2://Disconnected
            ds_list_delete(iplist,plrindex);
            ds_list_delete(portlist,plrindex);
            ds_map_delete(playersmap,"afk"+string(ip)+string(port));
        break;
        case 3://Chat
            var txt = buffer_read(tbuff,buffer_string);
            for(var f=0;f<ds_list_size(iplist);f+=1){
                var buff = buffer_create(128,buffer_grow,1);
                buffer_seek(buff,buffer_seek_start,0);
                buffer_write(buff,buffer_u8,4);
                buffer_write(buff,buffer_string,txt);
                network_send_udp(socket, ds_list_find_value(iplist,f), ds_list_find_value(portlist,f), buff, buffer_tell(buff));
                buffer_delete(buff);
            }
        break;
        case 4://Afk
            ds_map_replace(playersmap,"afk"+string(ip)+string(port),2);
        break;
        case 5://Boss defeated
            var name = buffer_read(tbuff,buffer_string);
            bosslvl += 1;
            if(bosslvl > 100){
                bosslvl = 1;   
            }
            bossdef3 = bossdef2;
            bossdef2 = bossdef1;
            bossdef1 = name;
            //Send boss stats to all
            for(var f=0;f<ds_list_size(iplist);f+=1){
                var buff = buffer_create(128,buffer_grow,1);
                buffer_seek(buff,buffer_seek_start,0);
                buffer_write(buff,buffer_u8,3);
                buffer_write(buff,buffer_u8,bosslvl);
                buffer_write(buff,buffer_string,bossdef1);
                buffer_write(buff,buffer_string,bossdef2);
                buffer_write(buff,buffer_string,bossdef3);
                network_send_udp(socket, ds_list_find_value(iplist,f), ds_list_find_value(portlist,f), buff, buffer_tell(buff));
                buffer_delete(buff);
            }
        break;
    }
}
 

Azenris

Member
whats in server alarm 2
do you use any
room_restart
game_restart

and have you inspected the size of your lists and maps, they are staying small and not growing infinately?
 

emicarra

Member
whats in server alarm 2
do you use any
room_restart
game_restart

and have you inspected the size of your lists and maps, they are staying small and not growing infinately?
network_set_config(network_config_enable_reliable_udp);
This is just called once in alarm 2

That's all the code there is, i don't use room_restart or game_restart, and the ds lists and maps sizes are shown in the draw event, they stay the size they should be, also i already checked if ds lists and maps was the problem, by deleting them at any moment, and see if the memory usage gets lower, but it doesnt.

I think there must be a small memory leak in some game maker function, maybe i should report this as a bug
 

samspade

Member
I'd still check and see how many lists and maps are being created. While, unfortunately, lists and maps are simply numbers and not a data type in GML you can still sort of loop over them and see how many exist:

Code:
var _counter, _array_of_lists;
_counter = 0;
_array_of_lists = [];
for (var i = 0; i < 10000; i+= 1) {
    if (ds_exists(i, ds_type_list) {
        _array_of_lists[_counter++] = i;
    }
}
show_debug_message(--_counter);
show_debug_message(_array_of_lists);
It might be worth running that code, and equivalent for ds_map, at a button press. This is assuming that GML always assigns the lowest available number to the newest datastructure, which it does reliably on Windows for me.
 
R

robproctor83

Guest
You should look at the profiler and see if things look correct, as in the right number of objects, surfaces, etc. Tracking down a missing data structure destroy can be a pain. It might be worth searching your code for every place you create a data structure and make sure you are destroying it appropriately. Don't forget other things like particles and buffers also have to be destroyed manually.
 

Azenris

Member
I dont see any missed buffer deleting :oops:

Only thing that catches my eye is in the Server Alarm event:you iterate over a list and delete entries in it at the same time, which may lead to some skipped entry checks.

Maybe it is a GMS thing :| From the code alone it looks ok, but for all I know, there is more secret code :mad:

Hopefully someone else can spot the error :eek:

EDIT: POST 100 !!!!!!!!!!! :cool::p:rolleyes:
 

emicarra

Member
I'd still check and see how many lists and maps are being created. While, unfortunately, lists and maps are simply numbers and not a data type in GML you can still sort of loop over them and see how many exist:

Code:
var _counter, _array_of_lists;
_counter = 0;
_array_of_lists = [];
for (var i = 0; i < 10000; i+= 1) {
    if (ds_exists(i, ds_type_list) {
        _array_of_lists[_counter++] = i;
    }
}
show_debug_message(--_counter);
show_debug_message(_array_of_lists);
It might be worth running that code, and equivalent for ds_map, at a button press. This is assuming that GML always assigns the lowest available number to the newest datastructure, which it does reliably on Windows for me.
Thanks for the code, i tried it and the amount of lists and maps are the needed, 2 lists, and 3 maps (i only create 1 map, but i guess those other 2 are created by game maker), but those numbers doesnt change

You should look at the profiler and see if things look correct, as in the right number of objects, surfaces, etc. Tracking down a missing data structure destroy can be a pain. It might be worth searching your code for every place you create a data structure and make sure you are destroying it appropriately. Don't forget other things like particles and buffers also have to be destroyed manually.
Thanks, I tried looking at the profiler but it wasn't of much help, there's only 1 object, 2 ds lists and 1 map i created, and all buffers are destroyed after being used, i posted my code in one of the replies

I dont see any missed buffer deleting :oops:

Only thing that catches my eye is in the Server Alarm event:you iterate over a list and delete entries in it at the same time, which may lead to some skipped entry checks.

Maybe it is a GMS thing :| From the code alone it looks ok, but for all I know, there is more secret code :mad:

Hopefully someone else can spot the error :eek:

EDIT: POST 100 !!!!!!!!!!! :cool::p:rolleyes:
Nice catch, that's definitely a mistake in my code, i added a "f -= 1" after deleting data in the map and lists so the code doesnt skip one check.
This wasn't the memory leak of course, but thanks for finding that error
 

emicarra

Member
Any chance that "network_set_config(network_config_enable_reliable_udp,socket);" may cause memory leak?
 
Top