kit icon indicating copy to clipboard operation
kit copied to clipboard

memory leak in adapter node with unresolved promises

Open lovasoa opened this issue 2 years ago • 4 comments

Describe the bug

Rendering a page that depends on a never-solving promise leaks memory.

Svelte seems to keep handlers around for the resolution of promises even after the page has been fully rendered and served to the client, preventing the js engine from freeing the associated memory.

Reproduction

https://github.com/lovasoa/sveltekit-memory-leak-bugreport

+page.svelte

<script>
    import c from './const';
</script>

{#await c.waitForMe()}
    <p>loading...</p>
{:then x}
    <p>loaded {x}!</p>
{:catch error}
    <p style="color: red">{error.message}</p>
{/await}

const.js

class MyClass {
    constructor() {
        this.x = neverSolve();
    }
    async waitForMe() {
        const x = await this.x;
        return x+1;
    }
}

function neverSolve() {
    return new Promise(()=>{});
}

export default new MyClass;

mem-graph

Logs

No response

System Info

System:
    OS: Linux 6.2 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
    CPU: (8) x64 11th Gen Intel(R) Core(TM) i7-11390H @ 3.40GHz
    Memory: 1.03 GB / 15.36 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 16.20.2 - /usr/bin/node
    Yarn: 1.22.19 - /usr/bin/yarn
    npm: 8.19.4 - /usr/bin/npm
    pnpm: 8.6.3 - /usr/bin/pnpm
  Browsers:
    Chrome: 116.0.5845.187
  npmPackages:
    svelte: ^4.0.5 => 4.2.0

Severity

annoyance

lovasoa avatar Sep 12 '23 14:09 lovasoa

Here are memory profiles from the example application, that can be examined with chrome devtools:

heaps.zip

image

lovasoa avatar Sep 12 '23 15:09 lovasoa

@dominikg , are you sure this belongs to sveltekit ? When I look at the code generated by svelte, it looks like it comes from there.

image https://svelte.dev/repl/251d77ae3761440b95abde98df3a2d78?version=4.2.0

/* App.svelte generated by Svelte v4.2.0 */
import { create_ssr_component, escape, is_promise, noop } from "svelte/internal";

const App = create_ssr_component(($$result, $$props, $$bindings, slots) => {
	const p = new Promise(() => {
			
		});

	return `${(function (__value) {
		if (is_promise(__value)) {
			__value.then(null, noop);
			return ``;
		}

		return (function (x) {
			return ` ${escape(x)} `;
		})(__value);
	})(p)}`;
});

export default App;

I'm not an expert, but the __value.then(null, noop) seems problematic to me. Doesn't it prevent deallocating __value even long after the component has been rendered ?

lovasoa avatar Sep 12 '23 16:09 lovasoa

If we don't do that, any rejecting promise that's unhandled will (in recent versions of Node) crash the whole server. I don't know that we have another option than to try to handle it in some way.

Conduitry avatar Sep 12 '23 16:09 Conduitry

any updates here?

SerovMihail avatar Feb 20 '24 10:02 SerovMihail