垃圾回收机制
为什么要关注内存?
- 防止页面内存占用过大,导致客户端卡顿,甚至没有响应
- Node使用的也是v8,内存对后端服务的性能至关重要。因为服务的持久性,后端更容易造成内存溢出。
栈空间
栈空间是临时空间,主要存储局部变量和函数调用。 基本类型赋值(Number/String/Boolean/Null/Undefined/Symbol/BigInt),系统会为新的变量在栈内存中分配一个新的值。 引用类型赋值,系统会为新的变量在栈内存中分配一个值,这个值仅仅是指向同一个对象的引用,和原对象指向的都是堆内存中的同一个对象。
对于函数,解释器创建了调用栈来记录函数的调用过程。每调用一个函数,解释器就把该函数添加到调用栈,解释器会为被添加进来的函数创建一个栈帧(用来保存函数的局部变量和执行语句)并立即执行。如果正在执行的函数还调用了其他函数,新函数会被继续添加到调用栈。函数执行完成,对应的栈帧立即被销毁。 栈溢出 栈不是可以无限增长的,被分配的调用栈空间被占满时,就会引起栈溢出的错误。 如下:
(function foo(){
foo()
})()
// Uncaught RangeError: Maximum call stack size exceeded
为什么基础数据存放在栈中,引用数据类型存放在堆中? js引擎需要用栈来维护程序执行期间的上下文状态,如果栈空间大了的话,所有数据都存放在栈空间里面,会影响到上下文切换的效率,进而影响整个程序的执行效率。
堆空间
大致分为新生代、老生代、大对象区、单元区属性单元区Map区、代码区。 垃圾回收算法 变量 => 新生代 =>老生代
------------------------------------------
| |
| Resident Set 所有内存占用 |
| |
| ---------------------------- |
| | 代码区域 Code Segment | |
| ---------------------------- |
| |
| ---------------------------- |
| | 栈(Stack):本地变量、指针 | |
| ---------------------------- |
| |
| ---------------------------- |
| | HeapTotal(堆):对象,闭包 | |
| | | |
| | --------------------- | |
| | |heapUsed 使用到的堆。| | |
| | | 判断内存泄漏, | | |
| | | 以heapUsed字段为准| | |
| | --------------------- | |
| | | |
| ---------------------------- |
------------------------------------------
新生代:
- 64位 共64mb from和to分别为32mb 32位折半
- copy复制 scavenge 算法
- from <=> to 互换
问:新生代为什么要采用复制这种形式呢? 算法一般考虑时间复杂度和空间复杂度,优先考虑时间复杂度,牺牲空间换时间。
老生代:
- 标记清除mark-sweep:标记、清除
- 标记整理mark-compact:标记、整理、清除
- 64位1.4GB 32位 7GB 14版的node内存为2GB
为什么要整理?为什么要先整理后清除? 整理后可以一次清除,整理的时候可以直接覆盖,也就清除了一部分垃圾。 广度扫描、全停顿标记、增量标记、三色标记 为什么需要连续的内存空间?需要大的空间存放大的变量 新生代如何晋升到老生代? 在新生代被复制过一次,to空间使用已超过25%,会晋升到老生代。 v8内存的使用情况
- 浏览器:window.performance.memory 属性
{
jsHeapSizeLimit: 4294705152
totalJSHeapSize: 41432924
usedJSHeapSize: 38893672
}
- node:process.memoryUsage() 函数
{
rss: 19550208, // 所有内存占用,包括指令区和堆栈
heapTotal: 5533696, // “堆”占用的内存,包括用到的和没用到的
heapUsed: 2645088, // 用到的堆的部分。判断内存泄漏,以heapUsed字段为准
external: 778349 // V8引擎内部的C++对象占用的内存
}
设置内存限制:
- -max-new-space-size 最大 new space 大小,执行scavenge回收,默认16M,单位KM
- -max-old-space-size,最大 old space 大小,执行MarkSweep回收,默认1G,单位MB
示例:
- var a = {name:‘yuhua’};这句代码会做如下几步:
- 将这句代码放入“代码区域 Code Segment”
- 将变量a放入“栈(Stack):本地变量、指针”
- 将{name:‘yuhua’}放入“HeapTotal(堆):对象,闭包”
- 注意:基本数据类型都在栈中,引用类型都在堆中
被标记后的变量是在内存快要满的时候才清理,示例如下:
onst memory = process.memoryUsage()
console.log(memory)
const getMemory = () => {
const memory = process.memoryUsage()
// console.log(memory)
console.log(`heapTotal: ${(memory.heapTotal / 1024 / 1024).toFixed(2)} heapUsed: ${(memory.heapUsed / 1024 / 1024).toFixed(2)}`)
}
const size = 20 * 1024 * 1024
let count = 0;
const useMem = () => {
var arr = new Array(size)
console.log(count++);
return arr
}
function fun() {
let arr1 = new Array(size)
let arr2 = new Array(size)
let arr3 = new Array(size)
let arr4 = new Array(size)
let arr5 = new Array(size)
}
fun()
var total = []
for (let i = 0; i < 12; i++) {
getMemory()
total.push(useMem())
}
console.log('success')
输出结果:
{
rss: 18718720,
heapTotal: 4509696,
heapUsed: 2390664,
external: 782946,
arrayBuffers: 9386
}
heapTotal: 819.70 heapUsed: 802.76
0
heapTotal: 835.70 heapUsed: 802.76
1
heapTotal: 995.71 heapUsed: 962.76
2
heapTotal: 1155.71 heapUsed: 1122.76
3
heapTotal: 1315.71 heapUsed: 1282.76
4
heapTotal: 1475.72 heapUsed: 1442.76
5
heapTotal: 1635.72 heapUsed: 1602.77
6
heapTotal: 1795.73 heapUsed: 1762.77
7
heapTotal: 1955.73 heapUsed: 1922.77
8
heapTotal: 2115.73 heapUsed: 2082.17 // 删除垃圾
9
heapTotal: 1635.47 heapUsed: 1602.17
10
heapTotal: 1795.48 heapUsed: 1762.17
11
success
评论(0)
登录后参与评论。
还没有评论,来抢沙发吧。

