释放变量所指向的内存_前端基础突破(二)内存回收与内存泄漏

垃圾回收的必要性?

在程序中,我们定义了许多变量,变量存储的在内存中,这时就需要一套机制来管理内存。在javascript中,并没有提供对应的api给开发者直接手动控制内存的申请和回收,因为javascript程序中的变量的内存空间的申请和释放都是javascript自动处理实现垃圾回收机制。

内存的垃圾回收机制是十分必要的。在程序中,字符串、数组和对象没有固定的大小,所以只有当他们大小定义好并已知时,才会动态的给他们分配内存存储空间,那么最终使用完之后都需要释放对应的内存,否则javascript持续分配却不释放内存的话,将会消耗完系统中所有可用的内存,造成系统崩溃。

垃圾回收机制

那么,javaScript垃圾回收的机制是怎样的呢?其实就是,找出不再使用的变量,然后释放其占用的内存,但是这个过程并不是实时的,因为实时轮询的开销会比较大,所以垃圾回收会按照固定的时间间隔周期性的执行。

那么垃圾回收机制怎么知道哪些内存不再需要呢?垃圾回收有两种方法:标记清除、引用计数。引用计数比较少用,在现代浏览器中不再使用,标记消除比较常用,现代浏览器使用的都是这种方式,本文主要讲解标记清除方式。

标记清除流程为:当变量进入执行环节时,就标记这个变量‘进入环境’,从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为当执行流进入响应的环境,就有可能会用到这些变量。当变量离开环境,则将其标记为‘离开环境’,然后在一定周期收,自动垃圾收集机制会把这部分内存回收释放。

比如:

5cbf58a6b67568ac73eb3d0d643b77aa.png

在上述代码中,a、b、c变量在执行add函数的时候,均被标记为‘进入环境’,当add函数执行完之后,则a、b、c变量均被标记为‘离开环境’,在一定周期之后,自动垃圾收集机制会把这部分变量占用的内存回收释放。

内存泄漏

虽然javascript已经有了垃圾回收机制,但是如果我们编写代码不恰当,那么很容易让变量一直被标记为‘进入环境’,一直无法回收,造成内存泄漏与浪费。

以下几种情况是比较容易造成内存泄漏的:

意外的全局变量

1fb9e86d64fa2357cefb8280ffa25bb5.png

在上述代码中,bar没声明就直接使用,在非严格模式下,会变成一个全局变量,一直占用内存不会被垃圾回收器释放。

还有另一种是函数中的this创建的:

c21e3a32c4c789c02014ec50639c2208.png

foo 函数调用的时候,在非严格模式下,this 指向了全局对象(window),意外的创建了全局变量。

要避免上述两种情况,只需要在 JavaScript 文件头部加上 'use strict',启用严格模式解析 JavaScript ,避免意外的全局变量。

计时器的疏忽

2a158cc81231c5b8808e7b444368b3b0.png

上述代码运行完之后,定时器还是会存在。同时由于setInterval回调函数中对someResource的引用,使得someResource占用的内存也不会被释放。

闭包(如果还没学过闭包,可先忽略这种情况)

由于IE的js对象和DOM对象使用不同的垃圾收集方法,因此闭包在IE中会导致内存泄露问题。

cc8fc75fa711707a50e67b81128bb974.png

代码片段做了一件事情:每次调用 replaceThing ,theThing 得到一个包含一个大数组和someMethod函数的新对象。同时,变量 unused 是一个引用 originalThing 的闭包(先前的 replaceThing 又调用了 theThing )。思绪混乱了吗?最重要的事情是,闭包的作用域一旦创建,它们有同样的父级作用域,作用域是共享的。someMethod 可以通过 theThing 使用,someMethod 与 unused 分享闭包作用域,尽管 unused 从未使用,它引用的 originalThing 迫使它保留在内存中(防止被回收)。当这段代码反复运行,就会看到内存占用不断上升,垃圾回收器(GC)并无法降低内存占用。本质上,闭包的链表已经创建,每一个闭包作用域携带一个指向大数组的间接的引用,造成严重的内存泄漏。

修复改例子闭包内存泄漏的方法很简单:

在 replaceThing 的最后添加 originalThing = null,标记originalThing,这样在其才会被垃圾回收器回收,释放占用的内存。

没有清理的DOM元素引用

ff516a3c5eb9898d452155efd6b61d73.png

在上述代码中,我们使用变量button获取一个dom节点之后,调用了web api去删除这个按钮。但是这个dom元素依然被button这个变量引用,所以对应的内存并没有被释放,换言之,DOM元素还在内存里面。此时只需要button =null即可释放内存。

如何识别内存泄漏

我们可以借助浏览器来识别内存泄露。

新版本的chrome在 performance 中查看:

58c34b4e3047b0aa7430ca7bf1ae96e3.png

步骤:

打开开发者工具 Performance

勾选 Screenshots 和 memory

左上角小圆点开始录制(record)

停止录制

在图中的红框,也就是HEAP部分,可以看到内存占用值在周期性的回落,也可以看到垃圾回收的周期。值的注意的是,如果垃圾回收之后最低值在不断上涨,那么肯定是有比较严重的内存泄露问题。

可通过垃圾回收优化代码的情景

数组优化

在编程中,有时候我们需要清空数组,大部分做法是arr =[],这种做法会创建一个新的空对象,会产生一小片内存垃圾。更加优化的做法是,直接将原来数组的长度设置为0即可,也就是arr.length = 0,这样既能清空数组,又能同时实现数组重用,减少内存垃圾的产生

尽量复用对象

在循环中,我们很容易创建新对象去记录某些值。优化的原则是,能复用的对象尽量复用,不用的对象,尽可能设置为null,使其可被垃圾回收。

942a3582d73b86c51d175e772be2f15f.png

通用的函数尽量放在循环外部

错误的示例

377d1bc925e13dbe1f3137a1193b7e28.png

优化的示例

083132b0691d4e5284f659ce89818669.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值