JS—垃圾回收

JS是使用垃圾回收的语言,即:执行环境负责在代码执行时管理内存。

基本思路:

  • 确定哪个变量不会再使用,然后释放它占用的内存。
  • 这个过程是周期性的,即垃圾回收程序每隔一定时间(或者说代码执行过程中某个特定的时间)就会自动运行。
  • 浏览器的发展史上,用到过两种主要的标记策略:标记清理引用计数

1.标记清理

JS最常用的垃圾回收策略

  • 当变量进入上下文,如在函数内部声明一个变量时,这个变量会被加上存在于上下文中的标记
  • 当变量离开上下文时,会被加上一个离开上下文的标记
  • 垃圾回收程序运行的时候,会标记内存中存储的所有变量。然后,它会将所有在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉。在此之后再被加上标记的变量就是待删除的了,原因是任何在上下文中的变量都访问不到它们了。随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存。

2.引用计数

没有那么常用。

思路是对每个值都记录它被引用的次数,以此判断是否需要清除

有一个严重的问题就是循环引用。即A引用B,B引用A。但是A和B是孤立存在的。没有被其他的引用。如果函数被多次调用,则会导致大量内存永远不会被释放

3.内存管理

  在使用垃圾回收的编程环境中,开发者通常午需关心内存管理。不过JS运行在一个内存管理与垃圾回收都很特殊的环境。分配给浏览器的内存通常比分配给桌面软件的要少很多,分配给移动浏览器的就更少了。这更多出于安全考虑而不是别的,就是为了避免运行大量 JavaScript 的网页耗尽系统内存而导致操作系统崩溃。这个内存限制不仅影响变量分配,也影响调用栈以及能够同时在一个线程中执行的语句数量。

  将内存占用量保持在一个较小的值可以让页面性能更好。优化内存占用的最佳手段就是保证在执行代码时只保存必要的数据。如果数据不再必要,要么把它设置为null,从而释放其引用——解除引用:

  • 解除引用最 适合全局变量和全局对象的属性。
  • 局部变量在超出作用域后会被自动解除引用
  • 注意:解除对一个值的引用并不会自动导致相关内存被回收。解除引用的关键在于确保相关的值已经不在上下文里了,因此它在下次垃圾回收时会被回收

(1)const和let声明变量来提升性能

ES6新增这两个关键字不仅有助于改善代码风格,而且同样有助于改进垃圾回收的过程。因为const和let都是块级作用域。使用这两个新关键字能更早地让垃圾回收程序介入,尽早回收应该回收地内存。在块级作用域比函数作用域更早终止地情况下,这就有可能发生。

(2)隐藏类和删除操作

  根据JS所在地运行环境,有时候需要根据浏览器使用地JS引擎来采取不的性能优化策略。

  Chrome使用V8引擎,V8将解释后的JS代码编译为实际的机器码时会利用“隐藏类”,这对代码性能非常重要:

  原理暂时没有看懂,见JavaScript高级程序设计内存管理-隐藏类

总而言之,两条要求:

  • 避免JS的先创建再补充式的动态属性赋值,并在构造函数中一次性声明所有属性
  • 把不想要的属性设置为null。可以保持隐藏类不变和继续共享

(3)内存泄漏

写得不好的JS可能出现难以察觉且有害的内存泄露问题。在内存有限的设备上,或者在函数会被调用很多次的情况下,内存泄漏可能是个大问题,JavaScript 中的内存泄漏大部分是由不合理的引用导致的。

常见造成内存泄漏的原因:

  • 意外声明全局变量:如没有使用关键字声明变量,解释器会把变量当作window的属性来创建
  • 定时器的回调
  • 闭包导致

(4)静态分配与对象池

说明:静态分配是优化的一种极端形式。如果你的应用程序被垃圾回收严重地拖了后腿,可以利用它提升性能。但这种情况并不多见。大多数情况,这都属于过早优化,因此不用考虑。

原文地址:https://www.cnblogs.com/codexlx/p/14277721.html