Closed ppyl-datBoi closed 1 week ago
Solution: After I removed {deep: true} in all "watch" functions the problem disappeared. But because of the many such "watch " now I will have to reconsider the logic in the application.
Could you please check the reproduction link? It seems to be inaccessible.
I can't provide full repro code because I have a complex commercial project from which it is difficult to extract repro code. The fact is that "watch" with {deep: true} starts to leak.
I also noticed that it's not just watch that's leaking. I have a couple of computed functions that are also leaking (the events array is updated several times per second). I compared several snapshots and noticed that the deps class is not being cleared, and that's a problem on your end.
Example of comparing snapshots 3 minutes apart:
We can't analyze the bug without a runnable minimal reproduction. Please follow the issue requirement.
I don't know if this mini demo is what you wanted to see, but even from it you can see that when comparing memory snapshots, the Dep class is not cleared by GC. But the computed leak could not be confirmed. Here is the link
Still unable to reproduce
I'm not sure if it is related but when debugging a reactivity issue in one of my vitest
tests I noticed an issue with the double-linked list for tracking dependencies:
depsTail
using prevDep
, there are 6 dependencies (not expected)deps
using nextDep
, there are 2 dependencies (expected)I wonder if a memory leak could be caused by the redundant Dep
objects referenced by depsTail.prevDep.prevDep.prevDep.*
? Maybe in some situations they could be accumulating much more than in my test code?
Unfortunately I'm still struggling to isolate the issue. :-/
PS. The issue I'm debugging is about a computed
not registering as a dependency of a shallowRef
, even though I can see that the track
function is called by Vue as expected.
Still unable to reproduce
- in Chrome incognito mode, ensure there are no browser extensions
- manually collect garbage
- JS heap size, DOM Nodes, JS event listeners. Their numbers will not continue to grow.
I also deliberately launched the demo in incognito mode. As you can see from the screenshot, the memory is gradually growing from snapshot to snapshot. Deps is also growing and is not cleared. Even deliberately calling the garbage collector from devtools does not improve the situation.
Thank you very much for this patch, it explains why all my applications made with the Quasar framework lagged, and the browser froze.
Hello, @yyx990803 @ppyl-datBoi : Version 3.5.5 had solved the problem, the new version 3.5.6 brought back the problem, but now my application made with Vue lag after 15 min of use, and crashes totally after 1h to 2h, is this a new memory leak?
Important note: this happens in dev mode, in release mode everything's fine (for now), I'm using the Quasar framework and Vite/yarn for the build.
@rxdiscovery Does the reproduction of this issue reproduce your problem? if not, please create a new issue with a minimal reproduction.
Might be related to c74bb8c2d
@rxdiscovery can you try the commit release here (set vue dependency in package.json to https://pkg.pr.new/vue@cbc39d5
) to see if it works?
Hello,
@yyx990803 I'll test the c74bb8c version and give you feedback as soon as possible.
I'm currently investigating the cause, there are 3 potential causes :
@edison1105 @yyx990803 I've also reported the problem here (the link below) with a video demonstration and the steps to reproduce it. https://github.com/quasarframework/quasar/issues/17520
(in the video, the first minute the application is reactive, but in minute 4 and +, it lags until it freezes.)
@rxdiscovery please also try the preview packages from https://github.com/vuejs/core/pull/11971 to confirm if it can resolve your problem. It fix a memory leak which happens in dev.
Hello,
to avoid any pollution, I created a new virtual machine with debian and the latest version of nodeJS and the latest version of Firefox. The same problem occurred
then I tested your suggestions:
@yyx990803
"dependecies" : {
...
"vue" : "https://pkg.pr.new/vue@cbc39d5"
...
}
@edison1105
"dependecies" : {
...
"vue" : "https://pkg.pr.new/vue@11971"
...
}
even if I force my Vue version, does quasar still use 3.5.5 internally?
Is the problem related to this issue? Or are we on the wrong track and I have to open another issue?
@rxdiscovery Please try to provide a minimal reproduction without quasar. I will take a deep look then.
@edison1105 There is a leak in Vue, it is located at the level of :
node_modules/@vue/shared/dist/shared.esm-bundler.js
Chrome memory profiling :
Firefox memory profiling :
it's like there's a loop !
perhaps the problem lies here:
const patchKeyedChildren = (c1, c2, container, parentAnchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized) => {
let i = 0;
const l2 = c2.length;
let e1 = c1.length - 1;
let e2 = l2 - 1;
while (i <= e1 && i <= e2) {
const n1 = c1[i];
const n2 = c2[i] = optimized ? cloneIfMounted(c2[i]) : normalizeVNode(c2[i]);
if (isSameVNodeType(n1, n2)) {
patch(
n1,
n2,
container,
null,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
);
} else {
break;
}
i++;
}
while (i <= e1 && i <= e2) {
const n1 = c1[e1];
const n2 = c2[e2] = optimized ? cloneIfMounted(c2[e2]) : normalizeVNode(c2[e2]);
if (isSameVNodeType(n1, n2)) {
patch(
n1,
n2,
container,
null,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
);
} else {
break;
}
e1--;
e2--;
}
if (i > e1) {
if (i <= e2) {
const nextPos = e2 + 1;
const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;
while (i <= e2) {
patch(
null,
c2[i] = optimized ? cloneIfMounted(c2[i]) : normalizeVNode(c2[i]),
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
);
i++;
}
}
} else if (i > e2) {
while (i <= e1) {
unmount(c1[i], parentComponent, parentSuspense, true);
i++;
}
} else {
const s1 = i;
const s2 = i;
const keyToNewIndexMap = /* @__PURE__ */ new Map();
for (i = s2; i <= e2; i++) {
const nextChild = c2[i] = optimized ? cloneIfMounted(c2[i]) : normalizeVNode(c2[i]);
if (nextChild.key != null) {
if (keyToNewIndexMap.has(nextChild.key)) {
warn$1(
`Duplicate keys found during update:`,
JSON.stringify(nextChild.key),
`Make sure keys are unique.`
);
}
keyToNewIndexMap.set(nextChild.key, i);
}
}
let j;
let patched = 0;
const toBePatched = e2 - s2 + 1;
let moved = false;
let maxNewIndexSoFar = 0;
const newIndexToOldIndexMap = new Array(toBePatched);
for (i = 0; i < toBePatched; i++)
newIndexToOldIndexMap[i] = 0;
for (i = s1; i <= e1; i++) {
const prevChild = c1[i];
if (patched >= toBePatched) {
unmount(prevChild, parentComponent, parentSuspense, true);
continue;
}
let newIndex;
if (prevChild.key != null) {
newIndex = keyToNewIndexMap.get(prevChild.key);
} else {
for (j = s2; j <= e2; j++) {
if (newIndexToOldIndexMap[j - s2] === 0 && isSameVNodeType(prevChild, c2[j])) {
newIndex = j;
break;
}
}
}
if (newIndex === void 0) {
unmount(prevChild, parentComponent, parentSuspense, true);
} else {
newIndexToOldIndexMap[newIndex - s2] = i + 1;
if (newIndex >= maxNewIndexSoFar) {
maxNewIndexSoFar = newIndex;
} else {
moved = true;
}
patch(
prevChild,
c2[newIndex],
container,
null,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
);
patched++;
}
}
const increasingNewIndexSequence = moved ? getSequence(newIndexToOldIndexMap) : EMPTY_ARR;
j = increasingNewIndexSequence.length - 1;
for (i = toBePatched - 1; i >= 0; i--) {
const nextIndex = s2 + i;
const nextChild = c2[nextIndex];
const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;
if (newIndexToOldIndexMap[i] === 0) {
patch(
null,
nextChild,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
);
} else if (moved) {
if (j < 0 || i !== increasingNewIndexSequence[j]) {
move(nextChild, container, anchor, 2);
} else {
j--;
}
}
}
}
};
the only thing we need is a minimal reproduction not screenshots
@edison1105 I'm currently analyzing the source code of Quasar's “QSelect”, to reproduce something similar in Vue, then I'll be able to give you a code without any dependencies.
@rxdiscovery try v3.5.7 by the way. We fixed some memory leaks yesterday.
@rxdiscovery try v3.5.7 by the way. We fixed some memory leaks yesterday.
I've already done the test, but unfortunately the problem persists.
Vue version
3.5.4
Link to minimal reproduction
http://cant.insert.code/commercial_project
Steps to reproduce
The problem started to appear in Vue 3.5.* (including version 3.5.4). There are no problems in version 3.4.38. In my case, the approximate source of the problem is an active websocket connection.
What is expected?
No memory leak
What is actually happening?
After establishing a socket connection from which data is coming, the memory grows infinitely. Moreover, the more socket connections, the more the memory grows without stopping. With 10 connections in 5-10 minutes, the heap size consumption is more than 3 GB. An example of how a leak starts after the application starts:
At the same time, taking memory snapshots is very difficult because the snapshot formation often gets stuck at the stage (Building dominator tree).
If you still manage to take a memory snapshot, it turns out that the preliminary cause is deps (I may be wrong).
I want to clarify that in case of a leak: DOM Nodes - do not change. JS Event Listeners - do not change.
System Info
Any additional comments?
No response