Js 内存问题

一直以来,对于Js的内存空间这部分的知识概念有些模糊,最近在回顾一些知识点的时候,特地的对js的内存这部分知识加深了一下理解,比如基本类型数据和引用类型数据在js内存中是怎么回事?什么是按值传递和按引用传递?以及对作用域和闭包的理解等等。

1 JavaScript的内存是怎样的?

JavaScript中有两种不同数据类型的值,一种是原始值,另外一种是引用类型值,原始值就是常说的基本数据类型值,包括String、Number、Boolean、Undefined和Null这五大基本数据类型,引用值指的是那些可能由多个指构成的对象,包括Object,Function,Array等类型的值。

JavaScript中的内存也分为栈内存和堆内存。一般来说,栈内存中存放的是存储对象的地址,而堆内存中存放的是存储对象的具体内容。对于原始类型的值而言,其地址和具体内容都存在与栈内存中;而基于引用类型的值,其地址存在栈内存,其具体内容存在堆内存中。堆内存与栈内存是有区别的,栈内存运行效率比堆内存高,空间相对堆内存来说较小,反之则是堆内存的特点。所以将构造简单的原始类型值放在栈内存中,将构造复杂的引用类型值放在堆中而不影响栈的效率。

我们看下下面Js的内存示意图:

  • var a = 20;

  • var b = 'abc';

  • var c = true;

  • var d = { m: 20 }

  • 可以看出变量a,b,c属于原始数据类型的变量,它们的值都存放在栈内存中,而d是一个对象即属于引用值,栈内存中存放的是它的内存地址,指向堆内存里面具体的一个值。

    接着我们来看下面的一段代码:

  • var a = {n:1};

  • var b = a;

  • a.x = a = {n:2};

  • console.log(a.x);// --> undefined

  • console.log(b.x);// --> [object Object]

  • 一开始看这道题,然后看答案,一脸懵逼啊有木有???为啥结果输出的是undefined和[object Object]???

    了解了Js的变量在内存的存储形式之后,我们一起来解释一下:

    1、a是一个引用类型的变量,一开始它在栈内存中的地址是指向堆内存的具体内容{n:1},接着赋值给b,所以b和a一样,此时都指向对象{n:1};

  • var a = {n:1} ;

  • var b = {n:1} ;

  • 2、接下来a.x = a = {n:2},我们都知道js的赋值运算是从右往左的,但“.”是优先级最高的运算符,所以这段代码先执行了a.x,所以此时对象{n:1}新增加了一个x的属性,并且值是undefined,所以运行到这里a和b都指向了对象{n:1,x:undefined};

  • var a = {n:1,x:undefined} ;

  • var b = {n:1,x:undefined} ;

  • 3、接着,依循“从右往左”的赋值运算顺序先执行 a={n:2} ,这时候,a指向的对象发生了改变,变成了新对象{n:2};而a.x = a则是对象{n:1,x:undefined}中的属性x指向了对象{n:2},所以此时指向的对象变成了{n:1,x:{n:2}}。

  • var a = {n:2} ;

  • var b = {n:1,x:{n:2}} ;

  • 综上所述,我们可以看到最后的运行结果,显然a.x是undefined,b.x是对象{n:2},用对象的字符串形式[object Object]表示。

    1.1 Js的内存空间管理

    JavaScript的内存分配和回收是自动完成的,满足一定条件,就会被垃圾回收器自动回收,下面我们简单的了解下js的内存管理机制。

    1.1.1 JavaScript的内存生命周期:

  • 分配你所需要的内存

  • 使用分配到的内存(读、写)

  • 不需要时将其释放、归还

  • var num = 10; // 在内存中给数值变量分配空间

  • alert(num); // 使用内存

  • num = null; // 使用完毕之后,释放内存空间

  • var obj = {v:1}; // 内存中存在{v:1}对象,及obj这个引用地址

  • obj = {value:2}; // 垃圾回收机制自动清理{v:1},并为新的有用到的{value:2}分配空间

  • 1.1.2 垃圾回收算法

    js垃圾回收有两种常见的算法:引用计数和标记清除。

    1.1.2.1 引用计数

    引用计数就是跟踪对象被引用的次数,当一个对象的引用计数为0即没有其他对象引用它时,说明该对象已经无需访问了,因此就会回收其所占的内存,这样,当垃圾回收器下次运行就会释放引用数为0的对象所占用的内存。

    但引用计数存在一个弊端就是循环引用问题(IE6和IE7就是采用此算法)。循环引用就是指对象A中包含一个指向对象B的引用,而对象B中也包含一个指向对象的引用。

  • function problem() {

  • var A = {};

  • var B = {};

  • A.a = B;

  • B.a = A;

  • }

  • 上面例子可以看出对象A和B存在循环音引用的问题,即两个的引用次数均为2,它们在运行之后依然存在,并且引用次数永远不为0,如果这个函数被多次调用,就有可能引起内存泄漏问题。为了解决循环引用的问题,还有一种方法就是可以实现垃圾回收,那就是标记清除法。

    1.1.2.2 标记清除

    标记清除法是现代浏览器常用的一种垃圾收集方式,当变量进入环境(即在一个函数中声明一个变量)时,就将此变量标记为“进入环境”,进入环境的变量是不能被释放,因为只有执行流进入相应的环境,就可能会引用它们。而当变量离开环境时,就标记为“离开环境”。

    垃圾收集器在运行时会给储存在内存中的所有变量加上标记,然后会去掉环境中的变量以及被环境中的变量引用的变量的标记,当执行完毕那些没有存在引用无法访问的变量就被加上标记,最后垃圾收集器完成清除工作,释放掉那些打上标记的变量所占的内存。

    标记清除之所以不存在循环引用的问题,是因为当函数执行完毕之后,对象A和B就已经离开了所在的作用域,此时两个变量被标记为“离开环境”,等待被垃圾收集器回收,最后释放其内存。

    1.1.3 管理内存

    使用具备垃圾收集机制的语言编写程序,开发人员一般都不必担心内存管理的问题。但JavaScript在进行内存管理以及垃圾收集时面临的问题还是有些不同。出于安全方面的考虑,系统分配给浏览器的可用内存数量通常要比分配给桌面应用程序的少,防止JavaScript的网页耗尽全部系统内存而导致系统崩溃。内存限制问题不仅会影响给变量分配内存,同时还会影响调用栈以及在一个线程中能够同时执行的语句数量。

    因此为了确保占用最少的内存可以让页面获取更好的性能。优化内存占用的最佳方式就是为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为null来释放其引用,即解除引用。这一做法适用于大多全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动被解除引用。(具体请阅读《JavaScript高级程序设计》第四章)

    可以分析以下代码:

  • function createPerson(name){

  • var localPerson = new Object();

  • localPerson.name = name;

  • return localPerson;

  • }

  • var globalPerson = createPerson("Junga");

  • globalPerson = null;//手动解除全局变量的引用

  • 在这个中,变量globalPerson取得了createPerson()函数的返回的值。在createPerson()的内部创建了一个局部变量localPerson并添加了一个name属性。由于localPerson在函数执行完毕之后就离开执行环境,因此会自动解除引用,而对于全局变量来说则需要我们手动设置null,解除引用。

    不过,解除一个值的引用并不意味着自动回收该值所占用的内存,解除引用真正的作用是让值脱离执行环境,以便垃圾收集器下次运行时将其收回。

    1.2 内存优化

    对于全局变量,JavaScript不能确定它在后面不能够被用到,所以它会从声明之后就一直存在于内存中,直至手动释放或者关闭页面/浏览器,这就导致了某些不必要的内存消耗。我们可以进行以下的优化:

    1.2.1 立即执行函数的运用

  • ;(function(window, $, undefined) {

  • // 主业务代码

  • })(window, jQuery);

  • 立即执行函数的作用就是建立一个独立的作用域,其一是为了防止全局污染,同时也可以防止过多的定义全局变量造成的内存回收问题。如果你的某些变量真的需要一直存在可以通过上面的方法挂载在window下。同样,你也可以传入jQuery进行使用。

    1.2.2 手动解除变量的引用

  • var obj = {a:1,b:2,c:3};

  • obj = null;

  • 1.2.3 使用回调

    除了使用闭包进行内部变量访问,回调函数也有这个功能。

  • function getData(callback) {

  • var data = 'Junga';

  • callback(data);

  • }

  • getData(function(data) {

  • console.log(data);

  • });

  • 回调函数是一种后续传递风格(Continuation Passing Style, CPS)的技术,这种风格的程序编写将函数的业务重点从返回值转移到回调函数中去。而且其相比闭包的好处也不少:

  • 如果传入的参数是基础类型(如字符串、数值),回调函数中传入的形参就会是复制值,业务代码使用完毕以后,更容易被回收;

  • 通过回调,我们除了可以完成同步的请求外,还可以用在异步编程中,这也就是现在非常流行的一种编写风格;

  • 回调函数自身通常也是临时的匿名函数,一旦请求函数执行完毕,回调函数自身的引用就会被解除,自身也得到回收。

  • 最后编辑于
    ©著作权归作者所有,转载或内容合作请联系作者
    • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
      沈念sama阅读 215,463评论 6 497
    • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
      沈念sama阅读 91,868评论 3 391
    • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
      开封第一讲书人阅读 161,213评论 0 351
    • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
      开封第一讲书人阅读 57,666评论 1 290
    • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
      茶点故事阅读 66,759评论 6 388
    • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
      开封第一讲书人阅读 50,725评论 1 294
    • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
      沈念sama阅读 39,716评论 3 415
    • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
      开封第一讲书人阅读 38,484评论 0 270
    • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
      沈念sama阅读 44,928评论 1 307
    • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
      茶点故事阅读 37,233评论 2 331
    • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
      茶点故事阅读 39,393评论 1 345
    • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
      沈念sama阅读 35,073评论 5 340
    • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
      茶点故事阅读 40,718评论 3 324
    • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
      开封第一讲书人阅读 31,308评论 0 21
    • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
      开封第一讲书人阅读 32,538评论 1 268
    • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
      沈念sama阅读 47,338评论 2 368
    • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
      茶点故事阅读 44,260评论 2 352

    推荐阅读更多精彩内容