【JavaScript】数据结构与内存中的堆和栈

目录

  • 前言
  • 一 堆栈
  • 二 堆栈与内存泄漏
  • 写在最后

前言

电脑中一个很重要的部件是内存条,绝大多数时候我们在操控一部分ram运行我们的程序,于是就涉及到了堆栈的概念,鉴于总是记不住要查,写一篇比较简单的来记一下堆栈。

一 堆栈

堆栈是一个计算机专用术语,在数据结构里它代表着两种数据的操作方式,在内存维度里代表着两种不同的缓存方式和分配。

1.1 数据结构中的堆栈

栈(stack): 相对来说大家比较熟悉一些,其数据的存取方式是先进后出,如同一个只有一个单元一层只有一户的筒子楼倒着看(奇怪的比喻,-_-||主要是想说明空间与门牌号关系的感觉),举个例子,js中的全局下文栈在运行时是第一个进(push)栈的,在浏览器关闭时成为最后一个出(pop)栈的。其他的函数上下文也是存储在栈中,但是一般都是即进(函数创建)即出(函数执行完毕)的,

堆(heap): 一种经过排序的树形结构,通常用来实现优先队列,有小顶堆和大顶堆两种,可以看作一个像树一样的书架,每本书的区域存放着一定特性的数据,可以直接拿放。(没具体写过)

1.2 存储空间中的堆栈(上下文与深浅拷贝)

栈区: 

前面特意用上下文的存取举了个栈结构的栗子,那么js中除了上下文引用类型,别的类型的变量都存在哪里呢?

首先可以确定的是基本类型都存放在栈区,该类型的数据存在该变量门牌号(地址)的房间里(数据值)。所以当你在拷贝一个值给另一个变量时,传递的是一个单纯的值,不会再产生任何指向运算。而当你在声明一个引用类型时,该变量所在的房间里存放的数据实际是一个地址值,该地址指向了内存中的另一块区域——堆区。

堆区:

当引用类型被浅拷贝时,因为所分配的数据空间是同一块,位于堆中的某一个位置。所以浅拷贝的数据值被所引用的引用类型变量所共同使用,一发则同全身。

而被深拷贝的时候,会根据执行重新分配一块新的内存空间,两个栈上的引用类型的变量指向运算后会指向堆中不同的地址空间。这个时候,大家各干各的,雨各自无瓜。

这样,在我们想要深拷贝的场景中,我们对引用类型拷贝时就不能使用直接赋值这种方式了,而是要重新分配一块空间给被拷贝值。

举个最简单的例子:

function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); } // 最常用场景使用

当然,如果遇到无函数可调用时,那么就需要手撸了,这里只提与本文最核心一行:

if (isType(parent, 'xType')) {   child = xTypeInstance}复制代码

二 堆栈与内存泄漏

2.1 内存泄漏

what

在某种场景下,对于分配的空间失去控制或者忘记释放,这些空间就属于泄漏掉的内存,在一些严重的递归或者是定时器场景中,内存泄漏会使浏览器内存占用越来越高,影响性能。

why

为什么会产生内存泄漏,因为那部分空间不能被垃圾回收机制所回收。

在IE9以前,有两种引用类型变量的垃圾回收策略,IE对于DOM对象会进行引用计数回收,而对于JS对象进行标记清除回收。

IE9以后浏览器对引用类型变量统一使用标记清除策略。

要去优化内存的时候,不用一定要记什么dom引用js对象,js引用dom对象,或者是计时器对象未被清除,闭包产生泄漏什么的,只需要理解标记清除的原理去分析就可以了,全局变量全部被标记为window引用,局部随着所在函数上下文出栈而失去引用被即刻释放,如果局部返回一个对象使仍然访问到局部中的某些变量,则这些变量就没有失去引用,自然就会继续占据内存空间。随着一些场景中对这些返回的对象的需求结束,那么我们就需要释放这些被绑在全局上的对象,则那些被对象所引用的变量的空间也会因为失去引用而被清除标记,随后在浏览器的gc中被回收。

How to release 

将该变量的引用赋值为null或者是将dom对象移除,在被多个对象引用时则都要处理才会释放空间。

写在最后

虽然上面列举了内存泄漏的原因和处理方式,但是其实我们应该注意的是去避免这些问题的出现,而有一些场景比如闭包产生的变量因为它是我们刻意去引用的,所以并不属于垃圾,不需要被回收。人为回收前其实更要在写代码时就注意避免不规范的写法以及不同场景变量是否属于垃圾的摇摆。


转载于:https://juejin.im/post/5cf3e42b5188254db16cd9d4

猜你喜欢

转载自blog.csdn.net/weixin_34115824/article/details/91434837