vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.66k stars 8.33k forks source link

Memory leak switch component by v-if ? #12306

Closed minaXU closed 1 week ago

minaXU commented 1 week ago

Vue version

3.5.12

Link to minimal reproduction

https://play.vuejs.org/#__PROD__eNp9VE2P0zAQ/SuDL+1KS1q0cCkpYkF7WA6AWI6WUEidrrepbdlOP1RF4rZcuAJnxBVuSIgDv4Ytf4Ox3aRJxUa9jOe9Gb8Zv3RDTpWKFgUjIxKbVHNlwTBbKMgTMR1TYg0lj6iggs+V1BbYXNk1ZFrOoRcN/MmV92qCZcaeZzUjHNuUDWiWQbnjOOghFakUxoJKpgzGDu/3WO/IXRwPgi6vIrZ4ZZ5YhieA+G1hrRTwOM15OkO1u3IsRdVeXDwInG6+dfwgtV0Qxl3c5VnNHo9D+zhMH3i7qQ+Jvm+8WwIyXVjrdz9yTPYbcm/Qnm/CFz5ohf54OAk3F5dyibPcCRHee/P+evvhy/bz779ff7Wm2rVQldy6Yg8C/Pn5bvv9x/bj9c23T42igWq2QFHYJJPadQEuINGaEhjN2NplsOVmA8ChLONBY5Y6rqKDvVRObHgwuBLRLg9VLqqX4XxkdcHQSAFBfZgWbAmnWifr/r3hcHgUZTzP+0MkNbzmXsZgUcan0ZWRAh9n4yRTksq54jnTL5Tl2JSSEXjEYUmey+Uzn3P3Hlf59JKls//kr8zK5Sh5qZlhesEoqTGb6CmzAT67eM5WGNfgXE6KHNkd4CtmZF44jYH2pBATlN3gebXnfqFcTF+bs5VlwlRDOaGOWXo+Jbjkpx2j7+WeRPd9HRUlbrH+j+iyd/jO3Hdzqy2w1ZsF004edjqJHkRDUv4Dm/SUNQ==

Steps to reproduce

  1. Click testIf.
  2. Click 切换展示.
  3. Click empty.
  4. Click force the GC in Chrome Devtools

What is expected?

The memory should go back to initial level

What is actually happening?

the memory heap keeps growing

System Info

No response

Any additional comments?

No response

edison1105 commented 1 week ago

This seems to be a Chrome bug.

  1. Create an HTML file with the following code.
  2. Open the HTML file and take a heap snapshot.
  3. Click the "show" button.
  4. Click the "test" button.
  5. Click the "remove" button.
  6. Click "GC" and take another heap snapshot.
  7. Compare the snapshots, there are several detached divs.

If step 4 is not performed, there will be no detached DOM elements generated.

<!doctype html>
<html lang="en">

<body>
    <button onclick="remove()">remove </button>
    <button onclick="show()"> show </button>
    <div id="container">
    </div>
    <script>
        function toggle() {
            console.log('toggle')
        }

        let btnEl
        function show() {
            const container = document.getElementById('container')
            container.innerHTML = `<div id="comp">
            <button id="btn">test</button>
            <div>1</div>
            <div>2</div>
            <div>3</div>
            <div>4</div>
            <div>5</div>
            </div>
            `
            btnEl = document.getElementById('btn')
            btnEl.addEventListener('click', toggle)
        }

        function remove() {
            btnEl.removeEventListener('click', toggle)
            btnEl = null

            document.getElementById('comp').remove()
        }
    </script>
</body>

</html>

Chromium issue https://issues.chromium.org/issues/376777343

linzhe141 commented 1 week ago

When I repeated the above reproduce steps and take heap snapshot, I found that there were many detached HTML elements, and these HTML elements were referenced by vnode

The above operations are all in the prod environment

image

chibx commented 1 week ago

This seems to be a Chrome bug.

  1. Create an HTML file with the following code.
  2. Open the HTML file and take a heap snapshot.
  3. Click the "show" button.
  4. Click the "test" button.
  5. Click the "remove" button.
  6. Click "GC" and take another heap snapshot.
  7. Compare the snapshots, there are several detached divs.

If step 4 is not performed, there will be no detached DOM elements generated.

<!doctype html>
<html lang="en">

<body>
    <button onclick="remove()">remove </button>
    <button onclick="show()"> show </button>
    <div id="container">
    </div>
    <script>
        function toggle() {
            console.log('toggle')
        }

        let btnEl
        function show() {
            const container = document.getElementById('container')
            container.innerHTML = `<div id="comp">
            <button id="btn">test</button>
            <div>1</div>
            <div>2</div>
            <div>3</div>
            <div>4</div>
            <div>5</div>
            </div>
            `
            btnEl = document.getElementById('btn')
            btnEl.addEventListener('click', toggle)
        }

        function remove() {
            btnEl.removeEventListener('click', toggle)
            btnEl = null

            document.getElementById('comp').remove()
        }
    </script>
</body>

</html>

This is one wild bug indeed. The fact that a single function that has no relationship with the container element, and still keeps it in memory is interesting

edison1105 commented 1 week ago

The Chromium team has confirmed that this is a regression. see https://issues.chromium.org/issues/376777343#comment9 Therefore, I will be closing this issue. Thank you for reporting it.