JS内存管理

JS中的内存管理,感觉就像JS中的一门副科,我们平时不太会忠实,但是一旦出现问题又很棘手,所以可以通过平时的多了解一些JS中的内存管理问题,在写代码中通过一些平时养成的习惯,避免内存泄露的问题。

不管什么语言,内存生命周期基本一致:

1、分配内存;

2、使用分配的内存(读、写);

3、不需要的时候再释放内存。

C语言中,有专门的内存管理接口,像malloc()和free()。JS中没有专门的内存管理接口,所有的内存管理都是自动的。JS创建变量是,自动分配内存,并在不使用的时候,自动释放该内存。这种自动的内存回收,造成了很多JS开发者并不关心内存回收问题,实际上,这会造成许多错误。

关于JS内存回收:

1、引用:

垃圾回收算法主要依赖于引用的概念,在内存管理环境中,一个对象如果有访问另一个对象的权限(隐式或显示),叫做一个对象引用另一个对象。例如:一个JavaScript对象具有对它模型的引用(隐式引用)和对它属性的引用(显示引用)

2、引用计数垃圾收集:

这是简单的垃圾收集算法,此算法吧“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用对象指向该对象,对象将被垃圾回收机制给回收。

示例:

let arr = [1,2,3];

arr = null;//[1,2,3]这时候没有被引用,会被自动回收

3:限制:循环使用:

在接下来的例子中,两个对象被创建并相互引用,就造成了循环引用。它们被调用之后不会离开函数作用域,所以它们已经没有用了,可以被回收了,然而,引用计数算法考虑到它们互相都有至少一次引用,所以不会被回收。

示例:

function f(){

var x = {};

var y = {};

x.p = y;//x引用y

y.p = x;//y引用x

//这里就形成了一个循环引用

}

f();

实际的例子:

var div;

window.onload = function(){

div = document.getElementById("myId");

div.circularReference = div;

div.lotsOfData = new Array(10000).join("*");

};

这个例子中,myId这个DOM元素里的circularReference属性引用了myId,造成了循环引用,IE6,7使用引用计数方式对DOM对象进行垃圾回收,该方式常常造成对象引用时内存发生泄漏。主流浏览器通过使用标记-清除 内存回收算法,来解决这一问题。

4:标记-清除算法

这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。

这个算法鉴定设置一个叫根root的对象(JS中,根是全局对象)。定期的,垃圾回收器将从根开始。找所有从根开始饮用的对象,然后找到这些对象引用的对面,从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。

从2012年起,所有的主流浏览器都使用了标记-清除内存回收算法。所有对JS垃圾回收算法的改进都基于该算法。

5:什么事内存泄露?

本质上讲,内存泄露就是不在被需要的内存,由于某种原因,无法被释放。

6:常见的内存泄露案例:

1:全局变量:

function foo(arg){

bar = "xx";

}

在JS中处理未被声明的变量,上述示例中的bar时,会把bar定义到全局对象中,在浏览器上也就是window上。在页面中的全局变量,只有当页面被关闭后才会被销毁。所以这种写法就会造成内存泄露,但让这个例子中泄露的只是一个简单的字符串,但是实际代码中,往往情况会更糟糕。

另外一种意外创建全局变量的情况:

function foo(){

this.varl = "xx";

}

foo();

foo被调用时,this指向全局变量window。意外的创建了全局变量,造成内存泄露。

我们谈到了一些意外情况下定义的全局变量,代码中也有一些我们明确定义的全局变量。如果使用这些全局变量来暂存大量的数据,记得在使用后,对其重新赋值为null。

2:未销毁的定时器和回调函数:

很多库中,如果使用了观察者模式,都会提供回调方法,来调用一些做后续处理的回调函数。要记得回收这些回调函数。

举一个setInterval的例子:

var serverData = loadData();

setInterval(function(){

var renderer = document.getElementById(‘renderer‘);

if(renderer){

renderer.innerHTML(JSON.stringify(serverData));

}

},5000);//5秒调用一次

如果后续renderer元素被溢出,整个定时器实际上没有任何作用,但是如果你不对其进行回收,整个定时器依然存在内存中,不但定时器无法被内存回收,定时器函数中的依赖也无法回收。在这个案例中的serverData也无法被回收。

3、闭包:

在JS开发中,我们会经常使用的闭包,一个内部函数,有权访问包含其的外部函数中的变量。

下面的这种情况,闭包也会造成内存泄露:

var theThing = null;

var replaceThing = function(){

var originalThing = theThing;

var unused = function(){

if(originalThing)//引用包含此函数的外部函数中的变量

console.log("x");

};

theThing = {

longStr:new Array(10000000).join(‘*‘),

someMethod:function(){

console.log("y");

}

};

};

setInterval(replaceThing,1000);

这段代码,每次调用replaceThing时,theThing获得了包含一个巨大的数组和一个对于新闭包someMethod的对象。同时unused是一个引用了originalThing的闭包。

这个范例的关键在于,闭包之间是共享作用域的,尽管unused可能一直没有被调用,但是someMethod可能会被调用,就会导致内存无法对其进行回收,这段代码被反复执行时,内存会持续的增长!

该问题的更多描述课件Meteor团队的文章!

4、DOM引用:

很多时候我们对DOM进行操作,会把DON的引用保存到一个数组或Map中

var elments = {

image:document.getElementById("image");

};

function doStuff(){

elements.image.src = ‘http://example.com/image_name.png‘;

}

function removeImage() {
    document.body.removeChild(document.getElementById(‘image‘));
    // 这个时候我们对于 #image 仍然有一个引用, Image 元素, 仍然无法被内存回收. 
}
上述案例中, 即使我们对于 image 元素进行了移除, 但是仍然有对 image 元素的引用, 依然无法对齐进行内存回收.
另外需要注意的一个点是, 对于一个 Dom 树的叶子节点的引用. 举个例子: 如果我们引用了一个表格中的 td 元素, 一旦在 Dom 中删除了整个表格, 我们直观的觉得内存回收应该回收除了被引用的 td 外的其他元素. 但是事实上, 这个 td 元素是整个表格的一个子元素, 并保留对于其父元素的引用. 这就会导致对于整个表格, 都无法进行内存回收. 所以我们要小心处理对于 Dom 元素的引用.
精读:
ES6中引入 WeakSet 和 WeakMap 两个新的概念, 来解决引用造成的内存回收问题. WeakSet 和 WeakMap 对于值的引用可以忽略不计, 他们对于值的引用是弱引用,内存回收机制, 不会考虑这种引用. 当其他引用被消除后, 引用就会从内存中被释放.
JS 这类高级语言,隐藏了内存管理功能。但无论开发人员是否注意,内存管理都在那,所有编程语言最终要与操作系统打交道,在内存大小固定的硬件上工作。不幸的是,即使不考虑垃圾回收对性能的影响,2017 年最新的垃圾回收算法,也无法智能回收所有极端的情况。
唯有程序员自己才知道何时进行垃圾回收,而 JS 由于没有暴露显示内存管理接口,导致触发垃圾回收的代码看起来像“垃圾”,或者优化垃圾回收的代码段看起来不优雅、甚至不可读。
所以在 JS 这类高级语言中,有必要掌握基础内存分配原理,在对内存敏感的场景,比如 nodejs 代码做严格检查与优化。谨慎使用 dom 操作、主动删除没有业务意义的变量、避免提前优化、过度优化,在保证代码可读性的前提下,利用性能监控工具,通过调用栈定位问题代码。
同时对于如何利用 chrome调试工具, 分析内存泄露的方法和技巧. 可以参考WEB前端教程-精读《2017前端性能优化备忘录》
总结:
即便在 JS 中, 我们很少去直接去做内存管理. 但是我们在写代码的时候, 也要有内存管理的意识, 谨慎的处理可能会造成内存泄露的场景.

原文链接:https://juejin.im/entry/59f9331551882546b15bd2bd?utm_source=gold_browser_extension

时间: 2024-10-12 04:36:58

JS内存管理的相关文章

Tensoflw.js - 02 - 模型与内存管理(易懂)

Tensoflw.js - 02 - 模型与内存管理(易懂) 参考 W3Cschool 文档:https://www.w3cschool.cn/tensorflowjs/ 本文主要翻译一些英文注释,添加通俗的注释,记录新手使用遇到的小问题,去除不必要的部分,帮助新手快速入门 上一篇介绍了,Tensorflow.js 的安装,张量与变量的表示方法.创建和输出 Tensoflw.js - 01 - 安装与入门(中文注释) 本篇介绍模型与内存管理 Tensorflow.js 模型: 1.在 Tenso

东哥学Node的故事——内存管理

前言 东哥是一个平凡的前端攻城狮,北邮网研院研二在读,刚接触Node不久,心里充满了对Node的好奇和崇拜,只听噗通一声,掉入了Node的坑... 于是东哥开始疯狂地看Node相关的书籍,这不,就学到了Node.js内存管理这一章. 他读到:“对于那些短时间执行的场景,比如网页应用.命令行工具,内存的管理似乎没有太大的必要.因为运行时间短,随着进程的退出,内存得到释放,几乎没有内存泄露,即使存在内存使用过多的情况,也只会影响到终端用户.所以,我们在使用JavaScript进行前端开发的过程中,很

JS内存泄漏 和Chrome 内存分析工具简介(摘)

原文地址:http://web.jobbole.com/88463/ JavaScript 中 4 种常见的内存泄露陷阱 原文:Sebastián Peyrott 译文:伯乐在线专栏作者 - ARIGATO 链接:http://web.jobbole.com/88463/ 点击 → 了解如何加入专栏作者 了解 JavaScript 的内存泄露和解决方式! 在这篇文章中我们将要探索客户端 JavaScript 代码中常见的一些内存泄漏的情况,并且学习如何使用 Chrome 的开发工具来发现他们.读

javascript 变量,作用域,内存管理小结

js的变量保存两种类型的数据——基本数据类型与引用类型.具有以下几点特征:   变量: 1)基本类型值在内存中占固定大小的空间,因此被保存在栈内存中; 2) 把保存基本类型值得变量赋给另一个变量,会创建这个值的副本; 3) 引用类型的值是对象,存在堆内存中; 4) 变量不会保存对象,只是创建了新的指针指向该对象,对象始终在堆内存中; 5) 指向对象的变量复制给一个新的变量,只是复制了指向对象的指针,最后两个变量都指向该对象; 6)  查看变量属于哪种基本类型得用typeof操作符,查看变量是哪种

Cocos2d之“引用计数”内存管理机制实现解析

一.引言 本文主要分析cocos2d游戏开发引擎的引用计数内存管理技术的实现原理.建议读者在阅读本文之前阅读笔者之前一篇介绍如何使用cocos2d内存管理技术的文章--<Cocos2d之Ref类与内存管理使用详解>. 二.相关概念 引用计数 引用计数是计算机编程语言的一种内存管理技术,是指将资源(对象.内存或者磁盘空间等)的被引用计数保存起来,当引用计数变为零时就将资源释放的过程.使用引用计数技术可以实现自动内存管理的目的. 当实例化一个类时,对象的引用计数为1,在其他对象需要持有这个对象时,

Unity 3D中的内存管理

本文欢迎转载,但烦请保留此行出处信息:http://www.onevcat.com/2012/11/memory-in-unity3d/ Unity3D在内存占用上一直被人诟病,特别是对于面向移动设备的游戏开发,动辄内存占用飙上一两百兆,导致内存资源耗尽,从而被系统强退造成极差的体验.类似这种情况并不少见,但是绝大部分都是可以避免的.虽然理论上Unity的内存管理系统应当为开发者分忧解难,让大家投身到更有意义的事情中去,但是对于Unity对内存的管理方式,官方文档中并没有太多的说明,基本需要依靠

cocos2dx 之内存管理

 cocos2dx的内存管理移植自Objective-C, 对于没有接触过OC的C++开发人员来说是挺迷惑的.不深入理解内存管理是无法写出好的C++程序的,我用OC和cocos2dx也有一段时间了,在此总结一下,希望对想用cocos2dx开发游戏的朋友有所帮助. C++的动态内存管理一般建议遵循谁申请谁释放的原则,即谁通过new操作符创建了对象,谁就负责通过delete来释放对象.如果对象的生命周期在一个函数内,这很容易做到,在函数返回前delete就行了.但一般我们在函数中new出来的对象

8、Cocos2dx 3.0游戏开发找小三之3.0版本的内存管理

重开发者的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27693365 复杂的内存管理 移动设备上的硬件资源十分有限,内存尤为宝贵,开发者必须十分慎重地利用内存,避免不必要的消耗,更要防止内存泄漏. 基于 Cocos2d-iPhone 的 Objective-C风格的内存管理是 Cocos2d-x 的一个特色. 把 Objective-C 的内存管理方式引入 C++,使得游戏开发的内存管理难度下降了个层次.

内存管理 &amp; 内存优化技巧 浅析

内存管理 浅析 下列行为都会增加一个app的内存占用: 1.创建一个OC对象: 2.定义一个变量: 3.调用一个函数或者方法. 如果app占用内存过大,系统可能会强制关闭app,造成闪退现象,影响用户体验.如何让回收那些不再使用的对象呢?本文着重介绍OC中的内存管理. 所谓内存管理,就是对内存进行管理,涉及的操作有: 1.分配内存:比如创建一个对象,会增加内存占用: 2.清除内存:比如销毁一个对象,会减少内存占用. 内存管理的管理范围: 1.任何继承了NSObject的对象: 2.对其他非对象类