浅谈JS内存管理

69 阅读3分钟

不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存的,不同的是某些编程语言需要我们自己手动的管理内存,某些编程语言会可以自动帮助我们管理内存。

不管以什么样的方式来管理内存,内存的管理都会有如下的生命周期:

  • 第一步:分配申请你需要的内存(申请)
  • 第二步:使用分配的内存(存放一些东西,比如对象等);
  • 第三步:不需要使用时,对其进行释放;

不同的编程语言对于第一步和第三步会有不同的实现:

  • 手动管理内存:比如C、C++,包括早期的OC,都是需要手动来管理内存的申请和释放的(malloc和free函数);
  • 自动管理内存:比如Java、JavaScript、Python、Swift、Dart等,它们有自动帮助我们管理内存;

垃圾回收

垃圾回收器:Garbage Collection,GC.对于那些不需要使用的对象需要回收、占用的内存需要释放。

那么GC是怎么实现判断哪些对象需要回收呢。GC概念最早出现在Lisp

引用计数(Reference counting)

当一个对象有一个引用指向它时,那么这个对象的引用就+1;当一个对象的引用为0时,这个对象就可以被销毁掉;

这个算法有一个很大的弊端就是会产生循环引用;

标记清除

标记清除的核心思路是可达性(Reachability)。

这个算法是设置一个根对象(root object),垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对于哪些没有引用到的对象,就认为是不可用的对象。

这个算法可以很好的解决循环引用的问题。

image.png

JS引擎比较广泛的采用的就是可达性中的标记清除算法,当然类似于V8引擎为了进行更好的优化,它在算法的实现细节上也会结合一些其他的算法。

标记整理(Mark-Compact)

标记整理(Mark-Compact)和“标记–清除”相似;不同的是,回收期间同时会将保留的存储对象搬运汇集到连续的内存空间,从而整合空闲空间,避免内存碎片化;

分代收集(Generational collection)

对象被分成两组:“新的”和“旧的”。

  • 许多对象出现,完成它们的工作并很快死去,它们可以很快被清理;(检查频率高)
  • 那些长期存活的对象会变得“老旧”,而且被检查的频次也会减少;

增量收集(Incremental collection)

如果有许多对象,并且我们试图一次遍历并标记整个对象集,则可能需要一些时间,并在执行过程中带来明显的延迟。

所以引擎试图将垃圾收集工作分成几部分来做,然后将这几部分会逐一进行处理,这样会有许多微小的延迟而不是一个大的延迟;

闲时收集(ldle-time collection)

垃圾收集器只会在CPU空闲时尝试运行,以减少可能对代码执行的影响。