持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
为什么要有内存管理
我们可以想一下,如果我们的软件或者代码,不做内存管理,那会是怎么样的呢,一个变量一直牢牢占据着计算机的内存,会越来越多,越来越多,目前计算机的运行内存大多数为16GB,如果不进行释放,那么你的电脑就会卡死。因此编程语言都是会对计算机的内存进行管理的
什么是内存管理呢
内存管理又称垃圾回收,就是编程语言将之前生成好的不同的内存,重新释放,以便下次使用
先来简单了解一下代码在计算机里的运行,以下面的js代码为例
var obj = {
name: 'juejin'
}
console.log(obj)
这个时候计算机做了这么几件事
- 申请:为obj申请一个堆内存
- 使用:分配使用这个堆内存
- 释放: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
}
再看个图片演示一下
编程语言的垃圾回收机制(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
}
来画一张图
由图可知,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的朋友分别是彼此的时候就会造成循环引用 来看一张图
我们来简单分析一下,此时kaka和ronaldo分别被引用了2次,如果我们接下来执行kaka = null,那么kaka的引用次数变为1,同理ronaldo=null,它的引用次数也减1,这样下来两个对象还是有一个引用计次数,就永远无法被回收。
为了解决这个问题,还有另一个算法叫,标记清除,我们一起来看下
标记清除
- 设置了一个根对象,gc会定期从根开始往前查找它的引用,对于那些没有引用的对象,和找不到的对象,进行回收,这就叫标记清除
- 这个算法很好的解决了循环引用的问题
v8引擎中应用的比较多的是标记清除算法,但是为了更高效率,v8引擎也是结合了一些其他的算法的