1. Hey! Guest! The 36th GMC Jam will take place between February 27th, 12:00 UTC - March 2nd, 12:00 UTC. Why not join in! Click here to find out more!
    Dismiss Notice

GML What is causing memory leak?

Discussion in 'Programming' started by emicarra, Jan 6, 2020.

  1. emicarra

    emicarra Member

    Joined:
    Jul 2, 2017
    Posts:
    60
    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
     
  2. nacho_chicken

    nacho_chicken Member

    Joined:
    Jun 21, 2016
    Posts:
    463
    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?
     
  3. emicarra

    emicarra Member

    Joined:
    Jul 2, 2017
    Posts:
    60
    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
     
  4. FrostyCat

    FrostyCat Member

    Joined:
    Jun 26, 2016
    Posts:
    4,851
    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.
     
  5. emicarra

    emicarra Member

    Joined:
    Jul 2, 2017
    Posts:
    60
    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;
        }
    }
     
  6. Azenris

    Azenris Member

    Joined:
    Oct 30, 2016
    Posts:
    111
    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?
     
  7. emicarra

    emicarra Member

    Joined:
    Jul 2, 2017
    Posts:
    60
    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
     
  8. samspade

    samspade Member

    Joined:
    Feb 26, 2017
    Posts:
    2,230
    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.
     
  9. robproctor83

    robproctor83 Member

    Joined:
    Sep 30, 2019
    Posts:
    290
    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.
     
  10. Azenris

    Azenris Member

    Joined:
    Oct 30, 2016
    Posts:
    111
    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:
     
    HayManMarc likes this.
  11. emicarra

    emicarra Member

    Joined:
    Jul 2, 2017
    Posts:
    60
    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

    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

    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
     
  12. emicarra

    emicarra Member

    Joined:
    Jul 2, 2017
    Posts:
    60
    Any chance that "network_set_config(network_config_enable_reliable_udp,socket);" may cause memory leak?
     

Share This Page