JS-垃圾机制

JS-垃圾机制

游戏|数码彩彩2024-03-05 7:36:00464A+A-

简介

JAVAScript 具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存。在编写JS的过程中,开发者不用关心内存的问题,所需内存的分配以及无 用内存的回收完全实现了自动管理。这种垃圾收集机制的原理其实很简单:找出那些不再继续使用的变 量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间), 周期性地执行这一操作。

内存的生命周期

内存分配:局部变量只在函数执行的过程中存在。而在这个过程中,会为局部变量在栈(或堆)内存上分配相应的空间,以便存储它们的值。

内存使用:然后在函数中使用这些变量,直至函数执行结束。

内存回收:此时,局部变量就没有存在的必要了,因此可以释放它们的内存以供将来使用

标记清除

JS 中最常用的垃圾收集方式是标记清除(mark-and-sweep) ,当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变 量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其 标记为“离开环境”。

由于被清理完的内存是不连续的,所以导致了后续存量大的对象可能无法正常存入内存当中,一般的处理方式都是在垃圾回收后进行整理操作,这种方法也叫 标记整理,整理的过程就是将不连续的内存向一端复制,使不连续的内存连续起来。目前主流浏览器的 JS 实现使用的都是 标记清除 式的垃圾收集策略(或类似的策略),只不过垃圾收集的时间间隔互有不同

举个
funtion example(){
 // 此时被标记,进入环境
 let a = 10
};
example(); 
// 函数执行完毕,此时a被标离开环境,被回收。
// 垃圾收集器 完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间 

引用计数

引用计数的含义是跟踪记录每 个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。 如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取 得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这 个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那 些引用次数为零的值所占用的内存

JS-垃圾机制

引用计数图例

举个
function example(){
 let a = 1; // a 初始化 count = 0
 let b = a; // b引用a,count = 1
 let b = 1; // b接触对a的引用,count = 0。当count重新为0时,内存回收a
}

手动管理内存

执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为 null 来释放其引用——这个 做法叫做解除引用(dereferencing)。这一做法适用于大多数全局变量和全局对象的属性。局部变量会在 它们离开执行环境时自动被解除引用

举个
function example(name){
 let localPerson = new Object();
 localPerson.name = name;
}
var globalPerson = example("Nicholas"); // 手工解除 globalPerson 的引用
globalPerson = null; 
// 解除globalPerson引用,让值脱离执行环境,垃圾收集器下次运行时便会将其回收 

几种常见内存泄露及解决方案

  • 全局变量
原因:一个未声明的变量的引用在全局对象中创建了一个新变量。在浏览器的环境中,全局对象是window
解决办法:添加'use strict' 
  • 循环引用
原因:在js的内存管理环境中,对象 A 如果有访问对象 B 的权限,叫做对象 A 引用对象 B。引用计数的策略是将“对象是否不再需要”简化成“对象有没有其他对象引用到它”,如果没有对象引用这个对象,那么这个对象将会被回收 。

举个
function example() {
 let obj1 = {}; 
 let obj2 = {}; 
 obj1.a = obj2; // obj1 引用 obj2 
 obj2.a = obj1; // obj2 引用 obj1 
}
当函数 example 执行结束后,返回值为 undefined,所以整个函数以及内部的变量都应该被回收,但根据引用计数方法,obj1 和 obj2 的引用次数都不为 0,所以他们不会进入回收队列
解决方案:不使用时设为null
  • 闭包
闭包会造成对象引用的生命周期脱离当前函数的上下文,如果闭包如果使用不当,可以导致环形引用(circular reference),类似于死锁,只能避免,无法发生之后解决,即使有垃圾回收也还是会内存泄露。
  • 延时器/定时器
原因:使用setInterval/setTimeout ,使用完之后通常忘记清理
解决办法:clearInterval/clearTimeout

内存优化方案

  • 分代回收(Generation GC)

这个和 Java 回收策略思想是一致的。目的是通过区分「临时」与「持久」对象;多回收「临时对象区」(young generation),少回收「持久对象区」(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时。Chrome 浏览器所使用的 V8 引擎就是采用的分代回收策略。

JS-垃圾机制

分代回收示例图

  • 增量回收(Incremental GC)

这个方案的思想很简单,就是「每次处理一点,下次再处理一点,如此类推」。这种方案,虽然耗时短,但中断较多,带来了上下文切换频繁的问题。Firefox 浏览器所使用的 JavaScript 引擎就是采用的增量回收策略。

JS-垃圾机制

增量回收示例图

内存优化引用:https://juejin.im/entry/58650be8ac502e005ff7b1e3

点击这里复制本文地址 版权声明:本文内容由网友提供,该文观点仅代表作者本人。本站(https://www.angyang.net.cn)仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件举报,一经查实,本站将立刻删除。

昂扬百科 © All Rights Reserved.  渝ICP备2023000803号-3网赚杂谈