摘要: JS是如何回收内存的?

JavaScript深入浅出》系列

最近垃圾回收这个话题非常火,大家不能随随便便的扔垃圾了,还得先分类,这样方便对垃圾进行回收再利用。

其实,对于写代码来说,也有垃圾回收(garbage collection)这个问题,这里所说的垃圾,指的是程序中不再需要的内存空间,垃圾回收指的是回收这些不再需要的内存空间,让程序可以重新利用这些释放的内存空间。

手动管理内存

对于C这种底层语言来说,我们可以使用malloc()函数分配内存空间,当所分配的内存不再需要的时候,可以使用free()函数来释放内存空间。

#include <stdio.h>
#include <stdlib.h>
#define TRUE 1 int main ()
{
int *p, i, n, sum; while (TRUE)
{
printf ("请输入数组长度: ");
scanf ("%d", &n);
p = (int *) malloc (n * sizeof (int)); // 分配内存空间
sum = 0;
for (i = 0; i < n; ++i)
{
*(p + i) = i + 1;
sum += *(p + i);
}
printf ("sum = %d\n", sum);
free (p); // 释放内存空间
}
return 0;
}

示例代码很简单,输入一个整数n,程序计算1、2、3...n的和。大家可以在Online C Compiler上运行这段代码。

请输入数组长度: 36
sum = 666
请输入数组长度: 100
sum = 5050

如果我们不去调用free()函数释放内存的话,就会导致内存泄漏(memory leak)。每个while循环中,指针p都会指向新分配的内存空间。而p之前指向的内存空间虽然没用了,但是并不会被释放,除非程序退出。如果while循环一直执行下去的话,内存早晚不够用。

垃圾回收算法

如果让我们去手动管理内存,那不知道要写出多少BUG,内存分分钟用完。还好现代编程语言,比如Java, Python, Go以及JavaScript,都是支持自动垃圾回收的。也就是说,这些语言可以自动回收程序不再需要的内存空间,这样既减轻了开发者的负担,也有效避免了内存泄漏。

其实,早在C语言诞生之前的1960年,图灵奖得主John McCarthy就在Lisp语言中实现了自动垃圾回收算法。算法本身其实非常简单,标记那些程序访问不到的数据,回收它们的内存空间。但是,垃圾回收算法把程序员从硬件层(内存管理)解放出来了,这种理念还是很先进的。

对于垃圾回收算法来说,最困难的问题是如何确定哪些内存空间是可以回收的,即哪些内存空间是程序不再需要的,这是一个不可判定问题(undecidable problem)。所谓不可判定,就是没有哪个垃圾回收算法可以确定程序中所有可以回收的内存空间。

McCarthy简化了判定数据是否需要的问题,将其简化为判断数据是否能够访问。如果程序已经不能访问某个数据了,那这个数据自然是不再需要了。但是,这个逻辑反过来是不成立的,一些可以访问的数据也有可能其实程序已经不再需要了。

McCarthy的垃圾回收算法现在通常被称作Mark-and-Sweep,它是现在很多语言(Java, JavaScript, Go)的垃圾回收算法的原型。

JavaScript的垃圾回收算法

对于JavaScript来说,我们是不需要手动管理内存的,因为JavaScript引擎例如V8SpiderMonkey都会自动分配并回收内存。

比较古老的浏览器,比如IE6和IE7使用的垃圾回收算法是reference-counting:确定对象是否被引用,没有被引用的对象则可以回收。这个算法无法回收Circular Object,有可能会因此造成内存泄漏:

var div;
window.onload = function() {
div = document.getElementById('myDivElement');
div.circularReference = div;
div.lotsOfData = new Array(10000).join('*');
};

div对象的circularReference属性指向div本身,因此div对象始终“被引用”。如果使用reference-counting垃圾回收算法的话,则div对象永远不会被回收。最新的浏览器很早就不再使用reference-counting,因此Circular Object无法回收的问题也就不存在了。

目前,主流的浏览器使用的垃圾回收算法都是基于mark-and-sweep

  • root对象包括全局对象以及程序当前的执行堆栈;
  • 从root对象,遍历其所有子对象,能够通过遍历访问到的对象是可以访问的;
  • 其他不能遍历对象是不可访问的,其内存空间可以回收;

算法思想并没有超越McCarthy半个世纪之前的设计,只是在实现细节上做了大量的优化,V8的垃圾回收模块Orinoco大致是这样做的

  • 采用多线程的方式进行垃圾回收,尽量避免对JavaScript本身的代码执行造成暂停;
  • 利用浏览器渲染页面的空闲时间进行垃圾回收;
  • 根据The Generational Hypothesis,大多数对象的生命周期非常短暂,因此可以将对象根据生命周期进行区分,生命周期短的对象与生命周期长的对象采用不同的方式进行垃圾回收;
  • 对内存空间进行整理,消除内存碎片化,最大化利用释放的内存空间;

JS引擎的垃圾回收算法已经非常强大了,所以我们作为JavaScript开发者基本上感受不到它的存在。

观察JavaScript垃圾回收算法

我们通过Chrome开发者工具实际感受一下垃圾回收算法的效果。

测试1:

var str = new Array(100000000).join("*");

setInterval(() => {
console.log(str[0]);
}, 1000);

str是一个超长字符串,因此会占有不少的内存空间。代码里面写了一个setInterval,是为了让这段代码永远执行下去,程序不退出。这样的话,字符串str永远在使用中,永远是可以访问的,那它的内存空间就不会被回收。

我使用的是Chrome 75,在其开发者工具的Memory的Tab下,使用Take heap snapshot可以获取内存快照:

可知,内存占用了97MB,且我们可以在其中找到str这个超长字符串。

测试2

var str = new Array(100000000).join("*");

setInterval(() => {
console.log(str[0]);
}, 1000); setTimeout(() => {
str = "******";
}, 10000);

在setTimeout的回调函数中,我们对str进行了重新赋值,这就意味着之前的超长字符串就不可访问了,那它的内存空间就会被回收。

在代码运行10s之后,即str重新赋值之后进行快照:

可知,内存只占用了1.6MB,且我们可以在其中找到str字符串,它的长度只有6,因此占用的内存空间非常小。

想象一下,如果不再需要的内存空间不会被回收的话,1T的内存都不够用。

关于JS,我打算花1年时间写一个系列的博客JavaScript深入浅出,大家还有啥不太清楚的地方?不妨留言一下,我可以研究一下,然后再与大家分享一下。欢迎添加我的个人微信(KiwenLau),我是Fundebug的技术负责人,一个对JS又爱又恨的程序员。

参考

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用

版权声明

转载时请注明作者 Fundebug以及本文地址:

https://blog.fundebug.com/2019/07/03/javascript-garbage-collection/

JavaScript深入浅出第3课:什么是垃圾回收算法?的更多相关文章

  1. JavaScript深入浅出第5课:Chrome是如何成功的?

    摘要: Chrome改变世界. <JavaScript深入浅出>系列: JavaScript深入浅出第1课:箭头函数中的this究竟是什么鬼? JavaScript深入浅出第2课:函数是一 ...

  2. JavaScript深入浅出第4课:V8引擎是如何工作的?

    摘要: 性能彪悍的V8引擎. <JavaScript深入浅出>系列: JavaScript深入浅出第1课:箭头函数中的this究竟是什么鬼? JavaScript深入浅出第2课:函数是一等 ...

  3. JavaScript深入浅出第2课:函数是一等公民是什么意思呢?

    摘要: 听起来很炫酷的一等公民是啥? <JavaScript深入浅出>系列: JavaScript深入浅出第1课:箭头函数中的this究竟是什么鬼? JavaScript深入浅出第2课:函 ...

  4. JavaScript深入浅出第1课:箭头函数中的this究竟是什么鬼?

    <JavaScript 深入浅出>系列: JavaScript 深入浅出第 1 课:箭头函数中的 this 究竟是什么鬼? JavaScript 深入浅出第 2 课:函数是一等公民是什么意 ...

  5. JAVA虚拟机垃圾回收算法原理

    除了释放不再被引用的对象外,垃圾收集器还要处理堆碎块.新的对象分配了空间,不再被引用的对象被释放,所以堆内存的空闲位置介于活动的对象之间.请求分配新对象时可能不得不增大堆空间的大小,虽然可以使用的总空 ...

  6. JVM调优-Jva中基本垃圾回收算法

    从不同的的角度去划分垃圾回收算法. 按照基本回收策略分 引用计数(Reference Counting) 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回 ...

  7. 深入理解java虚拟机【垃圾回收算法】

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

  8. 记录JVM垃圾回收算法

    垃圾回收算法可以分为三类,都基于标记-清除(复制)算法: Serial算法(单线程) 并行算法 并发算法 JVM会根据机器的硬件配置对每个内存代选择适合的回收算法,比如,如果机器多于1个核,会对年轻代 ...

  9. JVM 垃圾回收算法

    在说垃圾回收算法之前,先谈谈JVM怎样确定哪些对象是“垃圾”. 1.引用计数器算法: 引用计数器算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候,计数器-1,当 ...

随机推荐

  1. woocommerce如何隐藏SKU

    有时我们不想在woocommerce网站前台显示SKU,如下图所示,因为sku一多整个排版可能会乱,那么要如何隐藏sku呢?随ytkah一起来看看 在当前主题的function.php文件中加入如下代 ...

  2. Spring Boot2.0+中,自定义配置类扩展springMVC的功能

    在spring boot1.0+,我们可以使用WebMvcConfigurerAdapter来扩展springMVC的功能,其中自定义的拦截器并不会拦截静态资源(js.css等). @Configur ...

  3. 微信小程序 - 支付(后端代码实现)

    小程序支付 业务流程时序图 官方文档 步骤: 1. Openid 在小程序初次加载的时候就已经获取(详情见 小程序登录) 2. 生成商户订单 1.商品信息由小程序端提供 2.提供支付统一下单接口所需参 ...

  4. C++面向对象程序设计学习笔记(1)

    基本概念 对象: 面向对象程序设计中,对象是描述其属性的数据以及对这些数据施加的一组操作封装在一起构成的统一体,每个对象都是由数据和操作代码两部分构成的. 类: 面向对象程序设计中,类是具有相同的数据 ...

  5. 复杂模拟 | 1017 模拟N个顾客M个柜台进行排队

    #include <stdio.h> #include <memory.h> #include <math.h> #include <string> # ...

  6. ORB-SLAM2初步

    一.ORB-SLAM简介 最近开始入坑SLAM,经过简单调研,各位大咖认为,目前最优秀的视觉SLAM系统是ORB-SLAM2,因此对ORB-SLAM2进行了学习. ORB-SLAM2是2015年提出的 ...

  7. C++中二分法之upper_bound()、lower_bound、binary_search()函数

    前言 数组.容器vector都适用,在头文件"algorithm"中 下面的例子是针对容器的,注意返回的是距离元素3最近的指针it,输出的是*it结果为元素4,假如我想得到位置而非 ...

  8. [清华集训2017]小 Y 和地铁(神奇思路,搜索,剪枝,树状数组)

    世界上最不缺的就是好题. 首先考虑暴搜.(还有什么题是从这东西推到正解的……) 首先单独一个换乘站明显没用,只用考虑一对对的换乘站. 那么有八种情况:(从题解偷图)         然后大力枚举每个换 ...

  9. [LeetCode] 105. Construct Binary Tree from Preorder and Inorder Traversal 由先序和中序遍历建立二叉树

    Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  10. 本地手动一步步搭建WNMP环境(nginx+php+mysql) Windows平台

    环境:Windows 10 x64 参考文章: WNMP完整教程      windows下PHP环境的搭建 我自定义安装后的目录结构: +WNMP ++MySQL_Server-8.0.13 ++n ...