其实在写这篇文章之前 , 我也想了很久 , 因为网上对这块的东西已经很多了 , 但有些读起来还是不容易让人理解 , 而且JS 中的内存管理, 我的感觉就像 JS 中的一门副科, 我们平时不会太重视, 但是一旦出问题又很棘手. 所以可以通过平时多了解一些 JS 中内存管理问题, 在写代码中通过一些习惯, 避免内存泄露的问题 。
文章插图
内容概要
- 内存的生命周期
- JS的内存回收
- 常见的内存泄露案例
文章插图
【深入浅出JS的内存管理机制】
不管什么程序语言 , 内存生命周期基本是一致的:
- 分配你所需要的内存
- 使用分配到的内存(读, 写)
- 不需要时将其释放/归还
JS 中的内存回收引用
垃圾回收算法主要依赖于引用的概念. 在内存管理的环境中, 一个对象如果有访问另一个对象的权限(隐式或者显式), 叫做一个对象引用另一个对象. 例如: 一个JAVAscript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用).
引用计数垃圾收集
这是最简单的垃圾收集算法.此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”. 如果没有引用指向该对象(零引用), 对象将被垃圾回收机制回收. 示例:
let arr = [1, 2, 3, 4];arr = null; // [1,2,3,4]这时没有被引用, 会被自动回收限制: 循环引用
在下面的例子中, 两个对象对象被创建并互相引用, 就造成了循环引用. 它们被调用之后不会离开函数作用域, 所以它们已经没有用了, 可以被回收了. 然而, 引用计数算法考虑到它们互相都有至少一次引用, 所以它们不会被回收.
function f() { var o1 = {}; var o2 = {}; o1.p = o2; // o1 引用 o2 o2.p = o1; // o2 引用 o1. 这里会形成一个循环引用}f();
文章插图
实际例子:
var div;window.onload = function(){ div = document.getElementById("myDivElement"); div.circularReference = div; div.lotsOfData = https://www.isolves.com/it/cxkf/yy/js/2019-12-06/new Array(10000).join("*");};在上面的例子里, myDivElement 这个 DOM 元素里的 circularReference 属性引用了 myDivElement, 造成了循环引用. IE 6, 7 使用引用计数方式对 DOM 对象进行垃圾回收. 该方式常常造成对象被循环引用时内存发生泄漏. 现代浏览器通过使用标记-清除内存回收算法, 来解决这一问题.
标记-清除算法
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”.
这个算法假定设置一个叫做根root的对象(在JavaScript里 , 根是全局对象). 垃圾回收器将从根开始, 找到所有从根开始引用的对象, 然后找这些对象引用的对象, 从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象.
从2012年起, 所有现代浏览器都使用了标记-清除内存回收算法 。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进.
文章插图
自动 GC 的问题
尽管自动 GC 很方便, 但是我们不知道GC 什么时候会进行. 这意味着如果我们在使用过程中使用了大量的内存, 而 GC 没有运行的情况下, 或者 GC 无法回收这些内存的情况下, 程序就有可能假死, 这个就需要我们在程序中手动做一些操作来触发内存回收.
什么是内存泄露?
本质上讲, 内存泄露就是不再被需要的内存, 由于某种原因, 无法被释放.
常见的内存泄露案例1. 全局变量
function foo(arg) { bar = "some text";}在 JS 中处理未被声明的变量, 上述范例中的 bar时, 会把bar, 定义到全局对象中, 在浏览器中就是 window 上. 在页面中的全局变量, 只有当页面被关闭后才会被销毁. 所以这种写法就会造成内存泄露, 当然在这个例子中泄露的只是一个简单的字符串, 但是在实际的代码中, 往往情况会更加糟糕.
推荐阅读
- 您的Ping命令真的用对了吗?
- 什么是三层交换机?
- 如何区分百兆网线和千兆网线?
- 帝国cms调用当前访问会员的ID并显示该id的会员信息
- 善用SQL排名函数,让您的查询飞的更精彩
- 大清王朝的十二个铁帽子王 大清铁帽子王
- 路由器避坑指南
- 解决并发问题,数据库常用的两把锁!
- 白茶饼发霉后撬掉发霉的部分 茶叶发霉还能喝吗
- 原来传统的母亲节是这天 母亲节是哪一天