重学javaScript (七)| javaScript的内存管理

489 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

为什么要有内存管理

我们可以想一下,如果我们的软件或者代码,不做内存管理,那会是怎么样的呢,一个变量一直牢牢占据着计算机的内存,会越来越多,越来越多,目前计算机的运行内存大多数为16GB,如果不进行释放,那么你的电脑就会卡死。因此编程语言都是会对计算机的内存进行管理的

什么是内存管理呢

内存管理又称垃圾回收,就是编程语言将之前生成好的不同的内存,重新释放,以便下次使用

先来简单了解一下代码在计算机里的运行,以下面的js代码为例

var obj = {
    name: 'juejin'
}
console.log(obj)

这个时候计算机做了这么几件事

  1. 申请:为obj申请一个堆内存
  2. 使用:分配使用这个堆内存
  3. 释放:cpu执行完之后之后,将内存释放

不同语言的内存管理

不同的编程语言有着不同的内存管理机制,对于内存的申请,使用和释放是不同的,但是大体分为主动管理自动管理两种

  • 手动管理:C、C++等一些接近底层的编程语言,都是需要手动来申请和释放内存(malloc函数用于申请内存、free函数用于释放内存)
  • 自动管理: Java、JavaScript、Python等一些高级编程语言,都是自动帮助我们管理内存的

注:手动管理是存在很大的缺陷的,手动管理内存对开发者的要求很高,开发者在书写业务代码的时候,还需要考虑内存的分配问题,一不小心对内存的管理不当,就会产生内存泄露,从而造成不可预计的后果

关于Javascript的内存管理

大家都知道,js的数据类型分为引用数据类型基本数据类型两种,那么它们在内存中是怎么分配的呢,来看一段代码

  • 基本数据类型:字符串(String)、数字 (Number)、布尔 (Boolean)、空(Null)、未定义(Undefined)、Symbol
    • 内存中:会在执行的时候直接分配一个栈空间
  • 引用数据类型:对象(object),函数(func),数组(array)等
    • 内存中:会在执行的时候划出一个堆空间,生成一个内存地址,变量引用这个内存地址
var player = 'kaka'
var team = {
    name: 'milan',
    number: 22
}

再看个图片演示一下

image.png

image.png

编程语言的垃圾回收机制(Garbage Collection

先来看下维基百科里的介绍

在计算机科学中,垃圾回收(英语:Garbage Collection,缩写为GC)是指一种自动的[存储器管理]机制。当某个程序占用的一部分内存空间不再被这个程序访问时,这个程序会借助垃圾回收算法向操作系统归还这部分内存空间。垃圾回收器可以减轻程序员的负担,也减少程序中的错误。

  • 从文章第二部分,关于内存管理的类型的表述中可知,如果进行手动管理内存的话,可能会出现各种问题,如内存泄露等
  • 高级的编程语言中都是有自己的垃圾回收机制的,如java的jvm中存在,js的v8引擎中也有。

我们了解了什么是GC,那么它是如何做到,找出不用的内存,进行回收并释放的呢?我们接着往下看

Garbage Collection的常见算法

  • 引用计数
  • 标记清除

引用计数

引用计数的例子

维基百科里这样说道

当创建一个对象的实例并在堆上申请内存时,对象的引用计数就为1,在其他对象中需要持有这个对象时,就需要把该对象的引用计数加1,需要释放一个对象时,就将该对象的引用计数减1,直至对象的引用计数为0,对象的内存会被立刻释放。

使用这种方式进行内存管理的语言:[Objective-C]

很好理解,我们直接上代码吧

var team = {
team:'Real Madrid'
}

var player1 = {
    name:'kaka',
    team:team
}
var player2 = {
    name:'C.Ronaldo',
    team:team
}

来画一张图

image.png

由图可知,team的引用次数为3,player1和player2的引用次数为1 根据定义可知,如果我们这个时候执行player1 = null,则它的引用次数会从1变成0,这个时候触发我们的引用计数的机制,player1的内存就会被释放,然后回收,随着player1的释放,team也失去了指向它的player1,因此它的引用次数会减少成2,同样的如果我们继续执行 player2 = null,则会重复player1的步骤,至此球员的两个对象的内存全部被释放

引用计数的弊端

引用计数也是有弊端的,当对象互相引用的时候,就会造成循环引用,还是来看一段代码

var kaka = {
    name:'kaka',
    friend:ronaldo
}

var ronaldo = {
    name:'ronaldo',
    friend:kaka
}

当kaka和ronaldo的朋友分别是彼此的时候就会造成循环引用 来看一张图

image.png

我们来简单分析一下,此时kaka和ronaldo分别被引用了2次,如果我们接下来执行kaka = null,那么kaka的引用次数变为1,同理ronaldo=null,它的引用次数也减1,这样下来两个对象还是有一个引用计次数,就永远无法被回收。

为了解决这个问题,还有另一个算法叫,标记清除,我们一起来看下

标记清除

  • 设置了一个根对象,gc会定期从根开始往前查找它的引用,对于那些没有引用的对象,和找不到的对象,进行回收,这就叫标记清除
  • 这个算法很好的解决了循环引用的问题

image.png

v8引擎中应用的比较多的是标记清除算法,但是为了更高效率,v8引擎也是结合了一些其他的算法的