TheQwertiest / foo_spider_monkey_panel

foobar2000 component that allows to use JavaScript to create CUI/DUI panels
https://theqwertiest.github.io/foo_spider_monkey_panel/
MIT License
270 stars 21 forks source link

Out of memory issue. #32

Closed Ottodix closed 6 years ago

Ottodix commented 6 years ago

Hi,

With the lastest release, a panel using too much memory seems to crash. I used to do a cover cache, i can't do it anymore with the lastest release. Here is a example of code which always crash on my setup, it create a array containing the covers of the 2000 first tracks, grabbed with utils.GetAlbumArtV2:

var nb_of_covers = 2000;
var image_cache = Array();
var populate_covers_timer = Array();
var FullLibraryList = null;

function populate_with_library_covers(start_items){ 
    if(start_items == 0){
        FullLibraryList = fb.GetLibraryItems(); start_items = 0;            
        gTime_covers = fb.CreateProfiler();         
    }       
    var current_item = start_items;
    gTime_covers.Reset();       
    var total = FullLibraryList.Count;
    while(current_item < total){
        if(current_item < nb_of_covers){
            image_cache[current_item] = utils.GetAlbumArtV2(FullLibraryList[current_item]);            
        }
        current_item++;
        //Set a timer to avoid freezing on really big libraries
        if(current_item%250 == 0 && gTime_covers.Time > 100){       
            populate_covers_timer[populate_covers_timer.length] = window.SetTimeout(function(){
                window.ClearTimeout(populate_covers_timer[populate_covers_timer.length-1]);
                populate_with_library_covers(current_item);
            }, 25);             
            return;
        }               
    }
}   
populate_with_library_covers(0);

Error message: Error: Spider Monkey Panel v1.0.4 ({9B3A2903-6468-463C-8579-15BBC1C4738F}) Out of memory: 1020262025/1000000000 bytes

TheQwertiest commented 6 years ago

Well, your panel uses up 1GB worth of memory :\ If you really want to store a lot of images you can increase the heap limit through advanced settings: Preferences>Advanced>Tools>Spider Monkey Panel>Maximum heap size.

PS: IMO, storing such amount of art images in memory is overkill =)

TheQwertiest commented 6 years ago

PPS: Maximum heap size is the only value that is safe to change in Advanced settings, I don't recommend changing anything else, since it might impact your panel performance hard in really unexpected ways.

TheQwertiest commented 6 years ago

PPPS: The limit is per process, not per panel, so adjust accordingly.

Ottodix commented 6 years ago

Thanks! On this code which isn't optimized at all, yes, it uses a lot of memory. But on my real code which is a lot more complicate, it load a smaller version of the covers little by little without freezing foobar, and it uses around 150Mo (for 3000+ albums). On my computer, the increase of ram is completely okey, and i can navigate between albums without any loading time for the covers. I increased this Maximum heap size, now i don't have "out of memory" errors, but the images are lost between panels, i used to send this array of covers from one panel to another using on_notify_data and a deep clone of the received object, but the resulting images are now... corrupt ? I don't know exactly, but gr.DrawImage() now crash with them (DrawImage failed: Object is not of valid type). It's new from this 1.04 version, with the very first version of spider monkey panel, it is working.

So for the moment, i can't use this new version if i want to keep this cover cache :/

TheQwertiest commented 6 years ago

it uses around 150Mo

It actually uses MUCH more. Images are not stored in your usual memory (i.e. the one that you can see via Task Manager), but rather kernel memory which is not counted towards fb2k memory usage (but counts towards total RAM usage). E.g. when your panel OOM'd the task manager might've displayed smth around 200mb usage in fb2k, but total RAM usage would've been 1.2Gb.

i used to send this array of covers from one panel to another using on_notify_data

You can't do this on SMP, this is a JS engine limitation: see doc for details. You'll have to move image the acquisition code to the panel in which you want to use said image.

TheQwertiest commented 6 years ago

On another note: one of the features that I'm working on might enable the deep clone of some native objects (like GdiImage). So your scenario might be possible in the future.

Ottodix commented 6 years ago

It's true that i was looking just at the amount of RAM used by foobar, i just tried looking at the RAM used by the computer instead of just foobar, and the increase isn't so big when i start foobar, it seems to be similar, a little bit more but ok (50Mo more). I don't cache the full covers, i cache only small jpeg versions

Then i'll keep the first version of SMP. Anyway, it's true that my code is quite specific to my configuration, i guess there isn't much people trying to build a cover cache across all their panels like me : )

TheQwertiest commented 6 years ago

Then i'll keep the first version of SMP

Well, native object transfer via NotifyOthers was never possible on SMP, so using first version won't help in your case :\ And I strongly advise against using the first version, since it has a lot of bugs that were fixed in the latest version: CHANGELOG

Ottodix commented 6 years ago

It is working, i use NotifyOthers() in order to send an array of GdiImage(), and i receive the array with on_notify_data(name,info) using images = iterationCopy(info)

Where iterationCopy is this function function iterationCopy(src) { let target = {}; for (let prop in src) { if (src.hasOwnProperty(prop)) { target[prop] = src[prop]; } } return target; }

Why did it break with the new version, or why did it work with the first one, i don't know, but it work :)

TheQwertiest commented 5 years ago

So your scenario might be possible in the future.

Done! Added constructors in the latest version, so your scenario is possible now (you still have to increase heap limit via advanced settings though):

// performs a deep-copy of `image_from_notify`, so it is available even outside of `on_notify_data` callback
let copy_img = new GdiBitmap(image_from_notify);