Tampermonkey / tampermonkey

Tampermonkey is the most popular userscript manager, with over 10 million users. It's available for Chrome, Microsoft Edge, Safari, Opera Next, and Firefox.
GNU General Public License v3.0
4.2k stars 418 forks source link

Slow memory leak when using `GM_xmlhttpRequest` #2009

Open rh314 opened 6 months ago

rh314 commented 6 months ago

Expected Behavior

When using GM_xmlhttpRequest, there should not be any memory leaks. We expect Javascript's garbage collector to clean up requests when completed.

Actual Behavior

There is a small memory leak on every call to GM_xmlhttpRequest.
E.g. when GM_xmlhttpRequest is called once every millisecond (to more easily reproduce the bug) then memory consumption climbs by a few megabytes every second.

Initially I thought it might be the lack/presence of handling on* events, but the leak happens either way.
I've also checked if setting the fetch parameter changes the outcome - again, no effect, same outcome.

Specifications

Script

First, run a small webserver locally on port 12300. (Anything goes, it just makes it easier to reproduce the leak locally)

Then, use the following script to reproduce the memory leak:

// ==UserScript==
// @name         Memory leak - reproduce issue
// @namespace    http://tampermonkey.net/
// @version      2024-03-21
// @author       You
// @match        https://github.com/Tampermonkey/tampermonkey/issues
// @icon         https://www.google.com/s2/favicons?sz=64&domain=github.com
// @grant        GM_xmlhttpRequest
// @connect      127.0.0.1
// ==/UserScript==

console.log('STARTING SCRIPT');
setInterval(() => {
    GM_xmlhttpRequest({
        method: 'GET',
        url: 'http://127.0.0.1:12300/',
    });
}, 1);
rh314 commented 5 months ago

I've updated the ticket description to remove "optional/commented code". I'll put the full optional/commented code in this comment.

// ==UserScript==
// @name         Memory leak - reproduce issue
// @namespace    http://tampermonkey.net/
// @version      2024-03-21
// @author       You
// @match        https://github.com/Tampermonkey/tampermonkey/issues
// @icon         https://www.google.com/s2/favicons?sz=64&domain=github.com
// @grant        GM_xmlhttpRequest
// @connect      127.0.0.1
// ==/UserScript==

console.log('STARTING SCRIPT');
// var xhr_cnts = {
//     onload: 0,
//     onloadstart: 0,
//     onerror: 0,
//     onabort: 0,
//     ontimeout: 0,
//     onprogress: 0,
//     onreadystatechange: 0,
// };
setInterval(() => {
    GM_xmlhttpRequest({
        method: 'GET',
        url: 'http://127.0.0.1:12300/',

        // fetch: true,  // NOTE: Memory leak happens with any value here

        // onload: response => {
        //     xhr_cnts.onload += 1;
        //     if (xhr_cnts.onload % 1000 == 0) {
        //         console.log(xhr_cnts);
        //     }
        // },
        // onloadstart: response => { xhr_cnts.onloadstart += 1; },
        // onerror: response => { xhr_cnts.onerror += 1; },
        // onabort: response => { xhr_cnts.onabort += 1; },
        // ontimeout: response => { xhr_cnts.ontimeout += 1; },
        // onprogress: response => { xhr_cnts.onprogress += 1; },
        // onreadystatechange: response => { xhr_cnts.onreadystatechange += 1; },
    });
}, 1);