Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Memory leak when switch page & all sort of things #78

Closed
Gabirel opened this issue Mar 31, 2020 · 26 comments
Closed

[Bug] Memory leak when switch page & all sort of things #78

Gabirel opened this issue Mar 31, 2020 · 26 comments
Labels

Comments

@Gabirel
Copy link

Gabirel commented Mar 31, 2020

当前版本:v0.9.6-1

描述:
目前已经好几天没有关机了,然后我发现NCM的renderer的内存奇高,有4.15G(昨晚上是4G,白天打开后就变成了4.15G)
image

如果重启,默认启动的内存占用只有67MB
image

@rocka
Copy link
Member

rocka commented Mar 31, 2020

我也觉得有内存泄漏,但是就是找不到究竟漏在哪了。现在用的组件库很有可能会漏内存,但是真的不好调试 ...

@rocka rocka added the bug label Mar 31, 2020
@Gabirel
Copy link
Author

Gabirel commented Mar 31, 2020

Electrion或者Javascript,没有类似于C++什么的内存泄漏的检测工具之类的嘛

@rocka
Copy link
Member

rocka commented Mar 31, 2020

Chrome 开发者工具就有 Memory Profiler ,但我不太会用 ...

在频繁切换页面的时候,比如在首页,点击推荐歌单进入歌单详情页面,等待加载完成后返回,重复多次,内存的增长速度会非常快,而且几乎不会下降。刚刚启动应用时,JavaScript heap 的用量大概是 12M ,打开歌单详情页面约 10 次后,就会增长到 60-80M ,而且放置很长时间后都不会下降。同时 Performance 中的 Nodes 和 Listeners 数量会由几百增长至几千甚至数万,而且并没有下降的趋势。

目前发现了一点:

<transition :name="transitionName">
<keep-alive :include="KeepAlive">
<router-view></router-view>
</keep-alive>
</transition>

这个 transition/keep-alive/router-view 的组合,会导致经过 router-view 的 DOM 元素无法被销毁。如果把 keep-alive 换成 div ,内存泄漏问题几乎立即消失,无论怎么切换页面, JavaScript heap 的用量都会维持在 16M 左右,过一段时间后慢慢降到 12M,而且 Nodes 和 Listeners 数量也会保持在 1000 以下,但页面间切换时的动画就没有了。

不知道到底是我写错了,还是这一套东西有 bug ,有待更仔细的研究 ...

@rocka
Copy link
Member

rocka commented Mar 31, 2020

这是开发者工具中 Memory 面板中一个 Heap snapshot 的部分结构,注意这张图的层级是反着的,最下面的是根元素,往上是它的属性。

image

更具体的细节:router-view 中的一个组件被 keep-alive 的 include 所包含,它被加到了 keep-alive 的 cache 中。在 router-view 切换到另外的路由时,被 cache 住的组件通过 parent.parent.children.children 间接引用到了另外路由的组件,而这个被引用的组件的 VNode 又引用了它自己的 DOM 子树,这导致所有经过 router-view 的组件的 DOM 子树均被第一个 keep-live 的组件所引用,都无法被回收。

@rocka
Copy link
Member

rocka commented Apr 1, 2020

确认是 keep-alive 的 bug ,详情见 vuejs/vue#9842 vuejs/vue-router#2549 ,已有 PR vuejs/vue#9875 vuejs/vue#9962 但是还没合并,目前的 workaround 应该是从 PR 里面复制一个 keep-alive 过来用吧 ...

@rocka rocka changed the title [Bug] 疑似存在内存泄漏问题 [Bug] Memory leak when switch page Apr 1, 2020
@rocka
Copy link
Member

rocka commented Apr 1, 2020

should be fixed by f12a5eb

@rocka rocka closed this as completed Apr 1, 2020
@Gabirel
Copy link
Author

Gabirel commented Apr 7, 2020

image

这个问题貌似还存在?400多MB的内存开销,算是内存泄漏吗?毕竟初始只有几十MB

@rocka
Copy link
Member

rocka commented Apr 7, 2020

还有很多地方在泄漏,而且涉及到 Vue 以及 Chromium 的 bug ,详见 vuejs/vue#10004 中的讨论。

vuejs/vue#10085 虽然修复了 VueComponent 的泄漏,但是 DOM Elements 仍然在泄漏,目前找不到好的解决方案。

@rocka
Copy link
Member

rocka commented Apr 12, 2020

在 Vue 的 Pull Request 里面翻了一下,找到了一些可以修复内存泄漏问题的 patch ,经过测试,虽然每次切换页面或者按播放/暂停按钮还是会泄漏几个 VueComponent ,但是很大的内存泄漏应该不会有了 ...

使用 patch-package 在 CI 打包之前应用 patch ,不出意外的话在 v0.9.18 应该就能生效。

使用的 patch 列表: f820d77

目前 patch 了 vue 以及 vue-resize ,而且 vue-resize 的 patch 内容已经被合并了, Akryum/vue-resize#62

@Gabirel
Copy link
Author

Gabirel commented Apr 14, 2020

从下午运行到现在,中途还休眠过好几次。然后内存泄漏已经这么高了。我基本上没有怎么动过这个(即,很少干预程序本身的任何行为,除了我点不喜欢以外)。貌似它自己在切换歌曲的适合就会产生泄漏。(我平时听私人FM为主)

image

因为我平时电脑基本上不关机的,就是合上盖子。所以如果按照这个形式运行下去(初始的时候只有80MB的内存,现在到了322MB),那么过了两天什么的还是会出现几个G的内存泄漏~

PS:已更新到最新版的0.9.18

@rocka rocka reopened this Apr 14, 2020
@rocka rocka changed the title [Bug] Memory leak when switch page [Bug] Memory leak when switch page & all sort of things Apr 14, 2020
@rocka
Copy link
Member

rocka commented Apr 22, 2020

终于又发现了一处内存泄漏,只要音乐在播放,下方的进度条组件在更新,就会一直漏 VueComponent ,研究了一会发现是这个东西泄漏了:

https://github.com/museui/muse-ui/blob/74f78dfe6ae64776cfbd620e594daf3924abded4/src/internal/transitions.js

还是个 functional component ,都不知道说什么好了 ...

改成一般的组件之后,就不漏了:

rocka/muse-ui@6777a89

马上发新版本,这下总该没问题了吧 ...

@Gabirel
Copy link
Author

Gabirel commented Apr 22, 2020

改成一般的组件之后,就不漏了:

rocka/muse-ui@6777a89

马上发新版本,这下总该没问题了吧 ...

这个不提交PR?还是说这个是临时处理,然后等官方修复再改过来?

马上发新版本,这下总该没问题了吧 ...

我马上开始测试,我明天反馈看看结果怎么样

@rocka
Copy link
Member

rocka commented Apr 23, 2020

这个不提交PR?还是说这个是临时处理,然后等官方修复再改过来?

Muse UI 已经一年多没人维护了,提交了 PR 也不会被合并了,只能自己 fork 一下用用,这样子 …

@Gabirel
Copy link
Author

Gabirel commented Apr 23, 2020

默认最大上限有预估的值吗?因为我看这个内存始终在上升,大概是3s会增加额外的0.1MB内存使用。目前我观察到的是维持在273MB左右的内存就不再上升了(可能是考察期比较短,大约有10来分钟)。请问这个数值是正常数值?

image

初始的内存使用如下:
image

如果正常的话,那我就两三天后再来看使用情况。

@rocka
Copy link
Member

rocka commented Apr 23, 2020

其实我觉得不是很正常,如果只是听音乐,别的什么都不做的话,不应该有特别明显的占用上升。

在不泄漏的情况下,会额外占用内存的只有播放列表和私人 FM 的存储,以及 keep-alive 的首页/歌词页/我的收藏页面。打开控制台看一下,是不是还开着 debug log ,有没有很多输出,如果有的话,执行一下

localStorage.removeItem('debug')

把它关掉

@Gabirel
Copy link
Author

Gabirel commented Apr 23, 2020

  1. 上一个我的comment更新了一下,我发现我把3s写成了0.3s。已经修改过来了

  2. 我看了控制台,应该是没有debug flag
    image

@rocka
Copy link
Member

rocka commented Apr 23, 2020

目前只能说是减缓内存泄漏的速度了,虽然 VueComponent 不泄漏了,又发现了 VNode 泄漏的情况 ...

Screenshot_20200423_212430

和之前 #78 (comment) 的情况差不多,也是 parent children 的循环引用 ... Vue 现在的工作重心都在 Vue 3 ,短时间内没人会管了,看我有没有机会读一读 Vue 的源码自己修一下了 ...

@Gabirel
Copy link
Author

Gabirel commented May 28, 2020

这个有什么更新么?经常性的需要杀掉进程降低内存消耗有点麻烦

@rocka
Copy link
Member

rocka commented Jun 1, 2020

终于!发现了 VNode 泄漏的原因,也找到了解决方法,这次 Muse UI 要背锅

https://github.com/rocka/muse-ui/blob/v3.0.7/src/internal/directives/click-outside.js#L7-L9

在 directive 的 bind 方法中,声明的 documentHandler 引用了 vnode ,然后把这个 handler 挂在了 document 上,自然直到 event listener 被移除, bind 时的那个 vnode 都不能被 GC 。

既然这个 directive 的目的是在 click-outside 的时候执行某些操作,那不如默认它的参数一定是一个 function 。所以没必要去判断 binding.expression 了,直接用 binding.value 的值:

var clickOutSide = {
  name: 'click-outside',
  bind (el, binding) {
    const documentHandler = function (e) {
      if (el.contains(e.target)) return;
      el[clickoutsideContext].bindingFn(e);
    };
    el[clickoutsideContext] = {
      documentHandler,
      bindingFn: binding.value
    };
    setTimeout(() => {
      document.addEventListener('click', documentHandler);
    }, 0);
  },

  update (el, binding) {
    el[clickoutsideContext].bindingFn = binding.value;
  },

  unbind (el) {
    document.removeEventListener('click', el[clickoutsideContext].documentHandler);
    delete el[clickoutsideContext];
  }
};

这样改完以后,内存占用终于平稳了:

image

@Gabirel
Copy link
Author

Gabirel commented Jun 1, 2020

🤣 我靠,太强了吧。这个啥时候可以更新啊,我好赶紧去下载新版本

@rocka
Copy link
Member

rocka commented Jun 1, 2020

刚发了新版本,应该可以下载了

@rocka
Copy link
Member

rocka commented Jun 3, 2020

image

连续测试了大概一个小时,主进程和渲染进程的内存用量都非常稳定,可以认为已经修好了?😅

@rocka rocka closed this as completed Jun 3, 2020
@Gabirel
Copy link
Author

Gabirel commented Jun 3, 2020

image

我目前看到的这个内存增长量,大概是这么多。差不多是两天没有关机的使用吧。不确定是否还会在缓慢的增长。(初始的时候有75MB左右,现在翻倍了)

我再观察观察几天,看看是否还会增长很多

@rocka
Copy link
Member

rocka commented Jun 3, 2020

GPU 进程是没法控制的,归 chromium 管, electron 能控制的就只有主进程(electron-netease-cloud-music)以及渲染进程(Renderer),之前的内存泄漏就是发生在渲染进程。

@Gabirel
Copy link
Author

Gabirel commented Jun 5, 2020

明白。现在已经用了几天没退出,Renderer的内存开销还是维持在110MB左右,我觉得这个算是解决了。👍

@zongzi531
Copy link

大佬好强 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants