【知识点】JS中的内存管理

var n = 123; // 给数值变量分配内存

var s = “azerty”; // 给字符串分配内存

var o = {

a: 1,

b: null

}; // 给对象及其包含的值分配内存

// 给数组及其包含的值分配内存(就像对象一样)

var a = [1, null, “abra”];

function f(a){

return a + 2;

} // 给函数(可调用的对象)分配内存

// 函数表达式也能分配一个对象

someElement.addEventListener(‘click’, function(){

someElement.style.backgroundColor = ‘blue’;

}, false);

有些函数调用结果是分配对象内存:

var d = new Date(); // 分配一个 Date 对象

var e = document.createElement(‘div’); // 分配一个 DOM 元素

有些方法分配新变量或者新对象:

var s = “azerty”;

var s2 = s.substr(0, 3); // s2 是一个新的字符串

// 因为字符串是不变量,

// JavaScript 可能决定不分配内存,

// 只是存储了 [0-3] 的范围。

var a = [“ouais ouais”, “nan nan”];

var a2 = [“generation”, “nan nan”];

var a3 = a.concat(a2);

// 新数组有四个元素,是 a 连接 a2 的结果

JS 的内存使用

使用值的过程实际上是对分配内存进行读取与写入的操作。 读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。

var a = 10; // 分配内存

console.log(a); // 对内存的使用

JS 的内存回收

JS 有自动垃圾回收机制,那么这个自动垃圾回收机制的原理是什么呢? 其实很简单,就是找出那些不再继续使用的值,然后释放其占用的内存。

大多数内存管理的问题都在这个阶段。 在这里最艰难的任务是找到不再需要使用的变量。

不再需要使用的变量也就是生命周期结束的变量,是局部变量,局部变量只在函数的执行过程中存在, 当函数运行结束,没有其他引用(闭包),那么该变量会被标记回收。

全局变量的生命周期直至浏览器卸载页面才会结束,也就是说全局变量不会被当成垃圾回收。

因为自动垃圾回收机制的存在,开发人员可以不关心也不注意内存释放的有关问题,但对无用内存的释放这件事是客观存在的。 不幸的是,即使不考虑垃圾回收对性能的影响,目前最新的垃圾回收算法,也无法智能回收所有的极端情况。

接下来我们来探究一下 JS 垃圾回收的机制。

垃圾回收


引用

垃圾回收算法主要依赖于引用的概念。

在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。

例如,一个Javascript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)。

在这里,“对象”的概念不仅特指 JavaScript 对象,还包括函数作用域(或者全局词法作用域)。

引用计数垃圾收集

这是最初级的垃圾回收算法。

引用计数算法定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。 如果没有其他对象指向它了,说明该对象已经不再需了。

var o = {

a: {

b:2

}

};

// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o

// 很显然,没有一个可以被垃圾收集

var o2 = o; // o2变量是第二个对“这个对象”的引用

o = 1; // 现在,“这个对象”的原始引用o被o2替换了

var oa = o2.a; // 引用“这个对象”的a属性

// 现在,“这个对象”有两个引用了,一个是o2,一个是oa

o2 = “yo”; // 最初的对象现在已经是零引用了

// 他可以被垃圾回收了

// 然而它的属性a的对象还在被oa引用,所以还不能回收

oa = null; // a属性的那个对象现在也是零引用了

// 它可以被垃圾回收了

由上面可以看出,引用计数算法是个简单有效的算法。但它却存在一个致命的问题:循环引用。

如果两个对象相互引用,尽管他们已不再使用,垃圾回收不会进行回收,导致内存泄露。

来看一个循环引用的例子:

function f(){

var o = {};

var o2 = {};

o.a = o2; // o 引用 o2

o2.a = o; // o2 引用 o 这里

return “azerty”;

}

f();

上面我们申明了一个函数 f ,其中包含两个相互引用的对象。 在调用函数结束后,对象 o1 和 o2 实际上已离开函数范围,因此不再需要了。 但根据引用计数的原则,他们之间的相互引用依然存在,因此这部分内存不会被回收,内存泄露不可避免了。

再来看一个实际的例子:

var div = document.createElement(“div”);

div.onclick = function() {

console.log(“click”);

};

上面这种JS写法再普通不过了,创建一个DOM元素并绑定一个点击事件。 此时变量 div 有事件处理函数的引用,同时事件处理函数也有div的引用!(div变量可在函数内被访问)。 一个循序引用出现了,按上面所讲的算法,该部分内存无可避免的泄露了。

为了解决循环引用造成的问题,现代浏览器通过使用标记清除算法来实现垃圾回收。

标记清除算法

标记清除算法将“不再使用的对象”定义为“无法达到的对象”。 简单来说,就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。 凡是能从根部到达的对象,都是还需要使用的。 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。

从这个概念可以看出,无法触及的对象包含了没有引用的对象这个概念(没有任何引用的对象也是无法触及的对象)。 但反之未必成立。

工作流程:

  1. 垃圾收集器会在运行的时候会给存储在内存中的所有变量都加上标记。

  2. 从根部出发将能触及到的对象的标记清除。

  3. 那些还存在标记的变量被视为准备删除的变量。

  4. 最后垃圾收集器会执行最后一步内存清除的工作,销毁那些带标记的值并回收它们所占用的内存空间。

循环引用不再是问题了

再看之前循环引用的例子:

function f(){

var o = {};

var o2 = {};

o.a = o2; // o 引用 o2

o2.a = o; // o2 引用 o

return “azerty”;

}

f();

函数调用返回之后,两个循环引用的对象在垃圾收集时从全局对象出发无法再获取他们的引用。 因此,他们将会被垃圾回收器回收。

内存泄漏


什么是内存泄漏

程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。

对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。 否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。

本质上讲,内存泄漏就是由于疏忽或错误造成程序未能释放那些已经不再使用的内存,造成内存的浪费。

内存泄漏的识别方法

经验法则是,如果连续五次垃圾回收之后,内存占用一次比一次大,就有内存泄漏。 这就要求实时查看内存的占用情况。

在 Chrome 浏览器中,我们可以这样查看内存占用情况

  1. 打开开发者工具,选择 Performance 面板

  2. 在顶部勾选 Memory

  3. 点击左上角的 record 按钮

  4. 在页面上进行各种操作,模拟用户的使用情况

  5. 一段时间后,点击对话框的 stop 按钮,面板上就会显示这段时间的内存占用情况

  6. 来看一张效果图:

我们有两种方式来判定当前是否有内存泄漏:

  1. 多次快照后,比较每次快照中内存的占用情况,如果呈上升趋势,那么可以认为存在内存泄漏

  2. 某次快照后,看当前内存占用的趋势图,如果走势不平稳,呈上升趋势,那么可以认为存在内存泄漏

在服务器环境中使用 Node 提供的 process.memoryUsage 方法查看内存情况

console.log(process.memoryUsage());

// {

// rss: 27709440,

// heapTotal: 5685248,

// heapUsed: 3449392,

// external: 8772

// }

process.memoryUsage返回一个对象,包含了 Node 进程的内存占用信息。

该对象包含四个字段,单位是字节,含义如下:

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-sSmZxY3l-1715794647811)]

[外链图片转存中…(img-uJ0yDJpW-1715794647811)]

[外链图片转存中…(img-1I4RYbW3-1715794647812)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 9
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值