No clear answer for "computations created outside a `createRoot` or `render` will never be disposed"

I spent 5+ hours today reading about, running experiments and trying to find out exactly what warning message:
computations created outside a createRoot or render will never be disposed
actually means, but there is no definitive info. I get this message for some global createMemo, as well as expression inside the <Title> component. Global createMemo scenarios
const numWords = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven'];

// A1
const [num, setNum] = createSignal(3);

// B1
const [num, setNum] = createSignal(3);
const numString = createMemo(() => numWords[num() % numWords.length]);

// C1
const [num, setNum] = createSignal(3);
const numString = createMemo(() => numWords[num() % numWords.length]);
const numLetter = createMemo(() => numString().slice(0, 2).toUpperCase());
const numWords = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven'];

// A1
const [num, setNum] = createSignal(3);

// B1
const [num, setNum] = createSignal(3);
const numString = createMemo(() => numWords[num() % numWords.length]);

// C1
const [num, setNum] = createSignal(3);
const numString = createMemo(() => numWords[num() % numWords.length]);
const numLetter = createMemo(() => numString().slice(0, 2).toUpperCase());
in all three scenarios, signal and memos are created once globally. In such a case, scenarios 'B' and 'C' produce the said message. Question: for each of scenarios (A, B, C), does calling setNum causes an extra memory leak? Explanations are very appreciated, but please include definitive "yes/no" for each of A, B, C. I tried running an experiment calling setNum 10M+ of times and I don't know how to interpret the results: Microsoft's Edge Memory (as seen in Task Manager) grows to 8+Gb even for scenario "A", but sometimes it grows, sometimes it doesn't, some times it grew uncontrollably to 30+ Gb. After pausing the execution, it does garbage collection (and I force it in DevTools) and memory usage settles to lower number, but it's still always a few Gb - way more than initially.
12 Replies
Igor Konyakhin
Igor KonyakhinOP3w ago
Second group of scenarios
const numWords = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven'];

// A2
setInterval(() => {
const [num, setNum] = createSignal(3);
}, 1);

// B2
setInterval(() => {
const [num, setNum] = createSignal(3);
const numString = createMemo(() => numWords[num() % numWords.length]);
}, 1);

// C2
setInterval(() => {
const [num, setNum] = createSignal(3);
const numString = createMemo(() => numWords[num() % numWords.length]);
const numLetter = createMemo(() => numString().slice(0, 2).toUpperCase());
}, 1);
const numWords = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven'];

// A2
setInterval(() => {
const [num, setNum] = createSignal(3);
}, 1);

// B2
setInterval(() => {
const [num, setNum] = createSignal(3);
const numString = createMemo(() => numWords[num() % numWords.length]);
}, 1);

// C2
setInterval(() => {
const [num, setNum] = createSignal(3);
const numString = createMemo(() => numWords[num() % numWords.length]);
const numLetter = createMemo(() => numString().slice(0, 2).toUpperCase());
}, 1);
All setInterval are executed globally. Similar question: which of A, B, C will cause memory leakage? <Title> problem? Finally, I have this line:
<Title>{task() ? task().name : 'Tasks'}</Title>
<Title>{task() ? task().name : 'Tasks'}</Title>
which specifically also seems to cause the same message. When browsing the web, I stumbled somewhere that <Tasks> causes this problem, but there was nothing specific and no suggested resolution.
Simon Chan
Simon Chan3w ago
Creating signals won't cause memory leaks, neither setting signals. Signals are simple, independent objects Memos created as global variables won't be disposed, but all global variables won't be GCed anyway, so they are not memory leaks Creating both signal and memo in setInterval also won't cause memory leaks, because nothing references them. JS GC doesn't use reference counting, so they referencing each other won't prevent them to be GCed Creating memos that depending on a global signal (or a signal being referenced by other variables), without Solid owner, will cause a memory leak. Because the memo adds itself to the signal's reaction list, causing the signal to reference it and keep it alive For <Title>, it saves its children can execute later without owner (I had an issue that <Title>'s children can't read contexts in should belong to). Maybe it should capture the current owner and run its children in that. Anyway you can create a memo in your component and pass it to <Title>
Igor Konyakhin
Igor KonyakhinOP3w ago
Thanks for your case-by-case reply! Just to confirm: a) Warning "computations created outside a createRoot or render will never be disposed" is not "you have a memory leak", but no more than a warning? b) None of original 6 scenarios cause memory leak c) This:
const [num, setNum] = createSignal(3);
setInterval(() => {
const numString = createMemo(() => numWords[num() % numWords.length]);
}, 1);
const [num, setNum] = createSignal(3);
setInterval(() => {
const numString = createMemo(() => numWords[num() % numWords.length]);
}, 1);
will cause memory leak?
zulu
zulu3w ago
c. yes when you see that warning if you did not intended for something to linger you are likely need to "track it" so it is cleaned up when the parts that depends on it is cleaned up
Simon Chan
Simon Chan3w ago
Yes to all three. AlsocreateRoot(() => createMemo(...)) suppresses the warning, but the memo still won't be disposed (or GCed)
zulu
zulu3w ago
yes, it suppresses it, but also gives you a way to dispose of it
Igor Konyakhin
Igor KonyakhinOP3w ago
I think I understand: looking at the last 'c', it's clear where the leak comes from (since JavaScript doesn't have destructors similar to C++)
zulu
zulu3w ago
the leak comes from infinite interval that keeps on creating memos
Igor Konyakhin
Igor KonyakhinOP3w ago
Yeah, I meant that in C++ you can auto-destroy stuff, which goes out-of-scope (in this case numString), but there is no such thing in JavaScript, so it needs to be managed manually
zulu
zulu3w ago
yes. but createMemo by nature is a "long running" primitive and running in setInterval or setTimeout runs code in global scope( non trackable by solid) generally, solid keep tracks of memo and other effects, and auto dispose when the part that created in your application is destructed. if you see the warning it means you have to manually handle it yes. when you don't. you know that solid is able to track it and auto destruct it like C++
Igor Konyakhin
Igor KonyakhinOP3w ago
So, a fixed number of such warnings at startup for global memos are ok, but such warnings appearing repeatedly is a memory leak. Got it
zulu
zulu3w ago
yeah, that will be a good indicator. also if you want to see more how things work, or can work, you can check this playground. https://playground.solidjs.com/anonymous/768d5d53-d453-44eb-9d96-2b0719998e89
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template

Did you find this page helpful?