how to free menus memory

Posted on
  • hi,

    i'm getting tight with my memory usage so I started profiling it a little bit.

    i'm actually starting my app with a few menus and these guys require quite a lot of memory allocations: i need to decompress a few arrays using heatshrink.

    now, after i select my entry in the menu i can safely free all this memory and live happily ever after.
    i just need to keep a very tiny array i created in the callback.

    however i'm not sure how to do that. i don't see how to wait in the main function for the menu to return so i usually launch my code in the menu entry's callback itself. if i do that however, all large arrays stay allocated. i tried to use a timeout to create a new task and return from the menu callback which works but does not free my memory.

    i'm actually not even sure how the garbage collector is working.

    i'm not sure i'm clear. my code is here : https://github.com/wagnerf42/gps/blob/ma­in/app.js
    i want to call the function on line 357 with the local street array and free the memory decompressed line 321.

  • i'm actually not even sure how the garbage collector is working.

    Well as long as the value is referenced by some variable it cannot be freed.
    If you want to free memory locked by uncompressed_block before variable goes out of scope you can assign it another value like uncompressed_block=null

    however you should also clear raw_block (and raw_ways) as it is still referencing the uncompressed array too

    however all those functions defined at line https://github.com/wagnerf42/gps/blob/ma­in/app.js#L339 capture raw_ways in its scope so this is tricky. now it gets freed once menu goes out of scope (on line 361) as it references all those functions

  • yeah that's what i thought. if i declare a global "street" variable, set it in the menu handler and use setInterval in the main to detect for the variable being set then it effectively frees the menu's memory. however i expected it to be also true if using a setTimeout in the handler itself since i thought the timeout's function would not be associated with the menu (the menu's handler registers the timeout and completes) and this is not the case.

    so i have the setInterval workaround which is fine, i'm not stuck but somehow it still don't get why the garbage collector is not freeing the memory if i use a timeout.

  • still don't get why the garbage collector is not freeing the memory if i use a timeout

    can you show the code with timeout and where would you expect the memory to be freed?

  • yes, this guy : https://pastebin.com/D5k8NFhh

    is not freeing the memory.
    when i start the go_to method line 358 i have little memory remaining.
    however commenting out line 358 and removing comments 572-577 it works.

  • is not freeing the memory

    I guess all this runs inside line 361 E.showMenu(menu);
    It gets freed once it gets out of there (and all other references are gone too including code in the setTimeout).
    Anyway, the setTimeout on line 358 won't break you out of line 361(?)
    EDIT: oh I did not know E.showMenu(); without argument exits the menu, but still you can't have memory freed inside setTimeout as it still references all variables in parent scopes

  • and btw the lines 340-355 could be moved out of the function and precomputed earlier? that could break the reference to the uncompressed data.

    Also are you aware that on line 339 you define new function for each cycle of the loop? so you get many duplicated function definitions, can you define function once earlier and just reuse it there (and just pass j into it as a parameter or whatever is needed)?

  • I haven't looked into your code I'm afraid, but when memory isn't freed in menus, I find it's often because of something like this:

    function menuA() {
      console.log(process.memory().usage);
      E.showMenu({
       one : menuB
      })
    }
    
    function menuB() {
      console.log(process.memory().usage);
      E.showMenu({
       two : menuA
      })
    }
    
    menuA();
    

    The problem here is that E.showMenu calls functions with menu as an argument. Even though the argument isn't used in either menuA/menuB it's still there (in case some code in the function used arguments) so you're stuck with this chain of references.

    If you use () => menuA() instead of just menuA then that ditches the argument, and memory isn't leaked

    function menuA() {
      console.log(process.memory().usage);
      E.showMenu({
       one : () => menuB()
      })
    }
    
    function menuB() {
      console.log(process.memory().usage);
      E.showMenu({
       two : () => menuA()
      })
    }
    
    menuA();
    
  • @Gordon, I wonder how the interpreter works on this line https://github.com/wagnerf42/gps/blob/ma­in/app.js#L339
    it is called in a for loop - does the parser parse the function again each loop cycle or the second pass will reference previously parsed body only with different context with different variable values? or if the function would contain "jit" inside would it make separate copies of the method each loop iteration? how much wasteful this repetitive function definition it is regarding memory per each loop?

  • called in a for loop - does the parser parse the function again each loop cycle

    Yes, it does. But if the code if saved in flash, that new copy just references an address in flash, so it's not really using that much extra memory

  • thanks, so it is just burning extra CPU in this case but memory is not affected as you probably need to pass one such function for each menu item anyway no matter how big or small.

    Anyway, I tried to learn how E.showMenu() works. So I started Bangle 2 emulator, copy pasted example from http://www.espruino.com/Reference#l_E_sh­owMenu 'as is' and

    • the submenu item is broken - the }, ending part from back: line probably belongs to line above it but it still breaks with Uncaught Error: Cannot read property 'title' of undefined until I also comment out two items with undefined
    • when showing the menu repeatedly via E.showMenu(mainmenu) call and just clicking the Exit item I need to click twice to close it and everytime the menu is shown and hidden I get 5 variables less free visible in process.memory()

  • and btw I am not that good about javascript and its scoping rules so I tried this

    var val=0;
    
    function ExitMenu(args){
      E.showMenu();
      print("Exiting",val,args);
    }
    
    function click(args){
      print("Clicked",val,args);
    }
    
    function showMenu(){
      var val=1;
      function click2(args){
        print("Clicked",val,args);
      }
      function click3(args){
        click(args);
      }
      var mainmenu = {
        "" : { title : "-- Main Menu --" }, // options
        "One" : click,
        "Two" : click2,
        "Three" : click3,
        "four": { title:"Something", value: "x123", format: v => "", onchange:function(v){print("selected",v)­;setTimeout(ExitMenu,0,v);} },
    
        "Exit" : ExitMenu , // remove the menu
      };
      E.showMenu(mainmenu);
    }
    

    to see what value of val it will print for calling click,click2,click3 and the result is 0,1,0 - click2 takes local variable but click3 when calling click still prints the global variable. I guessed wrong the last case. Now I see that is what lexical scope really means.

    BTW what is the function arguments for in the function callback? could I somehow access the menu item so that I know which item was clicked = get its name or even some precomputed value/id I could store there when creating the menu? For now I see some object with "draw' and "scroller" properties, nothing related to clicked item.

    The nearest solution is the syntax at line "four" - if the value is string type and I override the format method it shows just title and I get id/value in the onchange. But this is a bit fragile as it depends on the harmless string type of the value for now, if the value is integer or boolean it does more stuff behind scenes.

  • Wow, thanks - that's a bit worrying. I've just put a fix in for that issue with :undefined for the type of the menu. At least in my Bangle I don't see the memory increase though.

    BTW what is the function arguments for in the function callback?

    It's just to be able to access the underlying menu (the same thing that E.showMenu(mainmenu) returns). But maybe it's not needed - if we had some way of checking apps to ensure ther weren't using it we could remove it, and it'd make it far less likely memory leaked

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

how to free menus memory

Posted by Avatar for wagnerf42 @wagnerf42

Actions