《JavaScript深刻浅出》系列:javascript
最近垃圾回收这个话题很是火,你们不能随随便便的扔垃圾了,还得先分类,这样方便对垃圾进行回收再利用。html
其实,对于写代码来讲,也有垃圾回收(garbage collection)这个问题,这里所说的垃圾,指的是程序中再也不须要的内存空间,垃圾回收指的是回收这些再也不须要的内存空间,让程序能够从新利用这些释放的内存空间。java
对于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,程序计算一、二、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引擎例如V8与SpiderMonkey都会自动分配并回收内存。
比较古老的浏览器,好比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:
算法思想并无超越McCarthy半个世纪以前的设计,只是在实现细节上作了大量的优化,V8的垃圾回收模块Orinoco大体是这样作的:
JS引擎的垃圾回收算法已经很是强大了,因此咱们做为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专一于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对一、微脉、青团社等众多品牌企业。欢迎你们免费试用!
转载时请注明做者 Fundebug以及本文地址: blog.fundebug.com/2019/07/03/…