azl397985856 / fe-interview

宇宙最强的前端面试指南 (https://lucifer.ren/fe-interview)
Apache License 2.0
2.83k stars 260 forks source link

【每日一题】- 2020-05-18 - V8中栈内存和堆内存的选择 #129

Closed azl397985856 closed 4 years ago

azl397985856 commented 4 years ago

V8中的内存管理是如何进行的? 什么样的会放到堆,什么样的会放到栈? 为什么要这样设计?

补充:

我们知道计算机科学并不是一门像数学那样的学科。 并没有什么绝对的对错。 而我上面举的例子是说V8这么做,或者更准确的说V8在某一个时期是这么做的,并不是说这种做法就是绝对的正确,其他做法是绝对的错误。 我这里想要问的其实是V8团队当时是如何考虑的?为什么要做这种设计?有什么权衡么?

suukii commented 4 years ago

放一下我浅显的回答。

Stack 是 V8 存放不可变数据的地方,包括函数调用栈原始类型的值,以及对象的引用,因为 JS 是单线程的,所以 V8 的实现也是每个 V8 进程只有一个 Stack

Heap 是 V8 存放对象或者说可变数据的地方,GC (Garbage Collection) 指的也是回收这块地方的内存。实际上 Heap 也分成了好几个部分:

就不展开了,我也不懂。以上部分可能有不同叫法,比如 New space (Young Generation) 和 Old space (Old Generation),这两个部分的区分大概就是为了优化 GC 的。

因为 Stack 中存放的数据都是一个叠着一个的,所以放在这里的数据一定是一开始就固定好不能改变的。如果把一个对象存在 Stack 中,之后再动态给对象增加属性,不就把对象后面位置的内存空间给覆盖了嘛(如果在创建对象和增加属性之间这段时间有往 Stack 中存了其他数据的话),所以对象作为“可变数据”是存放在 Heap 中的,Heap 我的理解是“一片混沌”,有什么东西往里面一塞就行了。

简单看一下一小段代码的执行以及在这个过程中 StackHeap 的变化:

stack_heap_v8

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

feikerwu commented 4 years ago

前置:

栈用于管理上下文,一个上下文环境包括变量环境和词法环境,变量都放置在变量环境,如果变量是基本类型,变量直接指向值,如果变量是对象,则变量指针指向对象的内存地址,对象数据被放在堆空间。

内存销毁

  1. 新生代使用Scavenge算法 将新生代的内存区域分为两块,一块处于使用记为from,另外一块处于闲置中记为to。当开始进行垃圾回收的时候,检查from空间的存活对象,将存活对象拷贝到to对象并做内存整理,避免引入过多的碎片。原来的from空间现在变为to空间,原来的to空间变为新的from空间。当某个对象在生存在经过两次置换后还存活在新生代,则该对象会采取晋升策略上升到老生代。

  2. 老生代采用增量标记算法 在老生代发生内存销毁事,V8会从根节点(全局上下文)开始遍历,标记不活动对象,标记后,销毁不活动对象,并对内存进行整理,避免生成内存碎片。由于js执行是单线程的,在内存销毁时,会停止其它的js脚本以及UI线程执行(stop-the-world),为了解决这个问题,v8引入了增量标记,将整个标记过程分片。不过分片的仅仅是增量标记的过程,销毁和整理的过程还是会阻塞到js的执行。

eric-gitta-moore commented 11 months ago

应该js的数据都在系统的堆区,但是如果看v8模拟的,那确实分了模拟堆区和模拟栈区