【深入探究Node】

    • 1. V8是用什么给对象分配内存的呢?
  • 2. V8为何要限制堆的大小?

  • 3. 原来如此,那你知道垃圾回收机制的策略是什么吗?

  • 4. 为什么要分代呢?

  • 5. 哦,那你谈谈是怎么分代的?

  • 6. 那 新生代是怎么回收的?

  • 7. 很好奇,一个新生代它是怎么晋升成老生代的。

  • 8. 为什么要设置25%这个这么低的值呢?

  • 9. 新生代的对象晋升后就成老生代了,那老生代为什么不能用Scavenge回收?

  • 10. 那老生代的对象该怎么处理?

  • 11. 那为什么还要标记整理?

  • 12. 咦!既然标记整理是基于标记清除上演变而来的,也就是它包括了标记清除,这么棒,那就用标记整理好了,干嘛还要说它结合标记清除使用呢?

  • 13. 原来是这样啊,要是垃圾回收算法时间花费很长,岂不是就要卡顿?

  • 14. 你知道Buffer对象吗?Buffer对象是通过V8分配内存的吗?

  • 15. 可以利用fs.readFile()和fs.writeFile()方法 来 读写大文件吗?

[](()1. V8是用什么给对象分配内存的呢?


在V8中,所有的JavaScript对象都是通过堆来进行分配的。Node提供了V8中内存使用量的查看方式,执行下面的代码,将得到输出的内存信息:

$ node

process.memoryUsage();

{ rss: 14958592,

heapTotal: 7195904,

heapUsed: 2821496 }

在上述代码中,在memoryUsage()方法返回的3个属性中,heapTotal和heapUsed是V8的堆内存使用情况,前者是已申请到的堆内存,后者是当前使用的量。至于rss为何,我们在后续的内容中会介绍到。图为V8的堆示意图:

当我们在代码中声明变量并赋值时,所使用对象的内存就分配在堆中。如果已申请的堆空闲内存不够分配新的对象,将继续申请堆内存,直到堆的大小超过V8的限制为止。

[](()2. V8为何要限制堆的大小?


表层原因为V8最初为浏览器而设计,不太可能遇到用大量内存的场景。对于网页来说,V8的限制值已经绰绰有余。

深层原因是V8的垃圾回收机制的限制。按官方的说法,以1.5 GB的垃圾回收堆内存为例,V8做一次小的垃圾回收需要50毫秒以上,做一次非增量式的垃圾回收甚至要1秒以上。这是垃圾回收中引起JavaScript线程暂停执行的时间,在这样的时间花销下,应用的性能和响应能力都会直线下降。这样的情况不仅仅后端服务无法接受,前端浏览器也无法接受。因此,在当时的考虑下直接限制堆内存是一个好的选择。

[](()3. 原来如此,那你知道垃圾回收机制的策略是什么吗?


V8的垃圾回收策略主要基于分代式垃圾回收机制。

[](()4. 为什么要分代呢?


因为在实际的应用中,对象的生存周期长短不一,不同的算法只能针对特定情况具有最好的效果。为此,现代的垃圾回收算法中按对象的存活时间将内存的垃圾回收进行不同的分代,然后分别对不同分代的内存施以更高效的算法。

[](()5. 哦,那你谈谈是怎么分代的?


在V8中,主要将内存分为新生代和老生代两代。新生代中的对象为存活时间较短的对象,老生代中的对象为存活时间较长或常驻内存的对象。图为V8的分代示意图。

[](()6. 那 新生代是怎么回收的?


在分代的基础上,新生代中的对象主要通过Scavenge算法进行垃圾回收。是一种采用复制的方式实现的垃圾回收算法。

它将堆内存一分为二,每一部分空间称为semispace。在这两个semispace空间中,只有一个处于使用中,另一个处于闲置状态。处于使用状态的semispace空间称为From空间,处于闲置状态的空间称为To空间。当我们分配对象时,先是在From空间中进行分配。当开始进行垃圾回收时,会检查From空间中的存活对象,这些存活对象将被复制到To空间中,而非存活对象占用的空间将会被释放。完成复制后,From空间和To空间的角色发生对换。

简而言之,在垃圾回收的过程中,就是通过将存活对象在两个semispace空间之间进行复制。

Scavenge的缺点是只能使用堆内存中的一半,这是由划分空间和复制机制所决定的。但Scavenge由于只复制存活的对象,并且对于生命周期短的场景存活对象只占少部分,所以它在时间效率上有优异的表现。

由于Scavenge是典型的牺牲空间换取时间的算法,所以无法大规模地应用到所有的垃圾回收中。但可以发现,Scavenge非常适合应用在新生代中,因为新生代中对象的生命周期较短,恰恰适合这个算法。

是故,V8的堆内存示意图应当如图所示。

当一个对象经过多次复制依然存活时,它将会被认为是生命周期较长的对象。这种较长生命周期的对象随后会被移动到老生代中,采用新的算法进行管理。对象从新生代中移动到老生代中的过程称为晋升

[](()7. 很好奇,一个新生代它是怎么晋升成老生代的。


《大厂前端面试题解析+Web核心总结学习笔记+企业项目实战源码+最新高清讲解视频》无偿开源 徽信搜索公众号【编程进阶路】

对象晋升的条件主要有两个,一个是对象是否经历过Scavenge回收,一个是To空间的内存占用比超过限制

在默认情况下,V8的对象分配主要集中在From空间中。对象从From空间中复制到To空间时,会检查它的内存地址来判断这个对象是否已经经历过一次Scavenge回收。如果已经经历过了,会将该对象从From空间复制到老生代空间中,如果没有,则复制到To空间中。这个晋升流程如图所示。

另一个判断条件是To空间的内存占用比。当要从From空间复制一个对象到To空间时,如果To空间已经使用了超过25%,则这个对象直接晋升到老生代空间中,这个晋升的判断示意图如图所示。

[](()8. 为什么要设置25%这个这么低的值呢?


设置25%这个限制值的原因是当这次Scavenge回收完成后,这个To空间将变成From空间,接下来的内存分配将在这个空间中进行。如果占比过高,会影响后续的内存分配。

[](()9. 新生代的对象晋升后就成老生代了,那老生代为什么不能用Scavenge回收?


对于老生代中的对象,由于存活对象占较大比重,再采用Scavenge的方式会有两个问题:一个是存活对象较多,复制存活对象的效率将会很低;另一个问题依然是浪费一半空间的问题。这两个问题导致应对生命周期较长的对象时Scavenge会显得捉襟见肘。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值