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

如何使用chrome查内存泄漏 #8

Open
zhuzhuaicoding opened this issue Jun 15, 2017 · 3 comments
Open

如何使用chrome查内存泄漏 #8

zhuzhuaicoding opened this issue Jun 15, 2017 · 3 comments

Comments

@zhuzhuaicoding
Copy link
Owner

zhuzhuaicoding commented Jun 15, 2017

缘由

vue一个issue,看到作者改动,只添加了一行代码(大神就是大神,仅仅一行代码,但是这行代码背后值得我去研究下),那么大神是怎么找到这个原因的呢?
我自己跟踪了这个issue里面的提供的reproduce link,从这个页面下手的。
想看内存是否泄漏了,可以从3个地方看:

  1. 任务管理器你看这个标签页下的内存占用,和js内存
  2. Timeline跟踪操作,在计数器窗口里查看js heap和Nodes的增长曲线,如果是成阶梯样,就有可能存在内存泄漏了(可以鼠标hover到对应的内存增长的陡点附近,定位这一时刻js都干了些什么)
  3. 使用Memory panel,查看内存快照

如何使用memory快照定位内存泄漏

Overview

直观感受:

  • 页面的性能随着时间的延长越来越差。 这可能是内存泄漏的症状。 内存泄漏是指,页面中的错误导致页面随着时间的延长使用的内存越来越多。
  • 页面的性能一直很糟糕。 这可能是内存膨胀的症状。 内存膨胀是指,页面为达到最佳速度而使用的内存比本应使用的内存多。
  • 页面出现延迟或者经常暂停。 这可能是频繁垃圾回收的症状。 垃圾回收是指浏览器收回内存。 浏览器决定何时进行垃圾回收。 回收期间,所有脚本执行都将暂停。因此,如果浏览器经常进行垃圾回收,脚本执行就会被频繁暂停。

任务管理器
Memory 原生内存,DOM节点存储在这里
JS Memory 列表示 JS 堆。此列包含两个值。 您感兴趣的值是实时数字(括号中的数字)。 实时数字表示您的页面上的可到达对象正在使用的内存量。 如果此数字在增大,要么是正在创建新对象,要么是现有对象正在增长。

Timeline
节点数是和代码对应的,如果内存慢慢增加,说明有泄漏。
Step:

  1. 打开record开关

  2. 按照issue里的复现步骤,点导航,点个几次

  3. 看计数器(主要看js heap和Nodes),可以看到曲线是在慢慢上升的(其实曲线是上升再下降的,成脉冲那种阶梯样,但是总的内存是在增加的,下降的原因是因为一些GC了)
    其实这个看也只是看个大概趋势,真正找到代码级别的内存泄漏的部分,还是得靠Memory Profile。

  4. 首先保存一个干净的内存快照

  5. 我点了3次不同的导航,在图里生成了3个detached dom(我用了Comparsion视图)

  6. 我随便点了其中一个,展开可以看到下面的retaining path
    4,可以看到下面的retainers窗口里有个pendingInsert这个对象
    2017-06-16 15-21-47

术语

  • Shallow size 对象自身的大小

  • Retained size 对象持有的引用,当被GC时,这一块大小会全部释放

  • Distance 到root的距离

  • Constructor 构造器 表示由这个构造器生成的同一类型的对象

  • Object count 对象的个数

  • 黄色背景的对象
    是js代码的直接引用,

  • 红色背景的对象

  • 未被js直接引用的,但是作为黄色节点的一部分而存活(本应该被GC的)
    黄色的节点,是存在内存泄漏的,需要看下为什么这个东西没有被GC

在下面的对象面板里查看具体哪个对象在引用上面的黄色节点

  • Object面板 查看Constructor面板中你选中的节点,在哪些地方被引用到了

  • 四种视图
    Summary --- 特别适合跟踪DOM泄漏
    Comparision
    Containment - 查看堆的内容,这里也可以看到detached dom tree,同样可以看到pendingInsert这个对象
    2017-06-16 21-07-01

Statistics
比较有用的就是Comparision了,比较2次快照,找到哪些对象本应该被GC的。

@frehaiku
Copy link

mark~

@liming9515
Copy link

mark

@zhuzhuaicoding
Copy link
Owner Author

zhuzhuaicoding commented Nov 2, 2018

看到 An interesting kind of JavaScript memory leak 这篇post
一时没搞懂这段代码为什么会导致内存泄露,这里记录一下分析结果

var theThing = null;
var replaceThing = function () {
  var originalThing = theThing;
  var unused = function () {
      if (originalThing)
      console.log("hi");
  };
  theThing = {
    longStr: new Array(1000000).join('*'),
    someMethod: function () {
      console.log(someMessage);
    }
  };
};
setInterval(replaceThing, 1000);

这段代码的执行过程是:每隔1s执行replaceThing,可以看到originalThing每次执行都是保存上一次的theThing,theThing是global变量,这样会theThing通过someMethod的lexical scope,引用上一次的originalThing,这样形成一条链,可以从图3看出

通过Memory的比较视图查看内存泄露原因

3
longStr是 theThing引用的
2
由于unused引用了free variable originalThing 形成了一个闭包,导致someMethod的Lexical Environment也引用了originalThing(因为someMthond和unused是共用一个parent lexical scope的),引用关系就是longStr -> originalThing(@91637) -> someMethod's closure context -> someMethod -> window.theThing (-> 表示 reference by)
1
上面的引用关系是longStr -> originalThing -> someMethod's closure context -> someMethod -> originalThing(对比上面一张图,可以看到就是@91637对象) -> someMethod's closure context -> ...同上

概念

(compiled code): 编译后的js代码
(array): 内部array对象
Array: array对象
Object: js普通对象
(closure): 闭包 === function.
system / Context: function context . 也就是 这个function的lexical environment
system: 内部数据

Context Variable

capture_2018112_170952
closure表示一个函数。
context variable是保存在function context中的变量。function context含有闭包的lexical environment ,其中含有这个闭包执行需要的变量。

Context

每个function在创建时,都会创建一个lexical environment,它存在一个parent context的引用。闭包创建的时候,v8会生成一个Context对象,这个对象里有所有这个闭包需要的绑定。
上面的someMethod的context对应的就是这个上下文。
capture_2018112_170716

function makeF() {
  var x = 11;
  var y = 22;
  return function (what) {
    switch (what) {
      case "x": return x;
      case "y": return y;
    }
  }
}

var f = makeF();
f("x");

contexts-0

Reference

@zhuzhuaicoding zhuzhuaicoding mentioned this issue Nov 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants