js内存数据

更新日期: 2020-06-17阅读: 2.2k标签: 内存

基本储存单元

位(bit):二进制数中的一个数位,可以是0或者1,是计算机中数据的最小单位。

字节(Byte,B):计算机中数据的基本单位,每8位组成一个字节。各种信息在计算机中存储、处理至少需要一个字节。例如,一个ASCII码用一个字节表示,一个汉字用两个字节表示。

字(Word):两个字节称为一个字。汉字的存储单位都是一个字。


扩展的存储单位

在计算机各种存储介质(例如内存、硬盘、光盘等)的存储容量表示中,用户所接触到的存储单位不是位、字节和字,而是KB、MB、GB等,但这不是新的存储单位,而是基于字节换算的。

KB:1KB=1024B
MB:1MB=1024KB
GB:1GB=1024MB
TB:1TB=1024GB

UTF-8编码:一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。中文标点占三个字节,英文标点占一个字节。

Unicode编码:一个英文等于两个字节,一个中文(含繁体)等于两个字节。中文标点占两个字节,英文标点占两个字节。


数组

数组最大长度 2^32 – 1(即4294967296 – 1),js数组并不是真正意义上的数组(栈)

在计算机科学中,数组数据结构(英语:array data structure),简称数组(英语:Array),是由相同类型的元素(element)的集合所组成的数据结构,分配一块连续的内存来存储。

JavaScript的数组是否分配连续内存取决于数组成员的类型,如果统一是单一类型的数组那么会分配连续内存,如果数组内包括了各种各样的不同类型,那么则是非连续内存。

非连续内存的数组用的是类似哈希映射的方式存在,它可以通过多种数据结构实现,其中一种是链表。比如声明了一个数组,他被分配给了1001、2011、1088、1077四个非连续的内存地址,通过指针连接起来形成一个线性结构,那么当我们查询某元素的时候其实是需要遍历这个线性链表结构的,这十分消耗性能。

现在, JavaScript 引擎已经在为同种数据类型的数组分配连续的存储空间了。另外,在 ES2015/ES6 中, 数组还有其它改进。 TC39 决定在 JavaScript 中引入类型化数组,所以如今我们有 ArrayBuffer了。ArrayBuffer 会有一大块连续的存储位置,你能用它做任何你想做的事情。不过,直接处理内存涉及非常底层的操作,相当复杂。


字符串

字符串理论上的最大长度是没有限制的,只要你的js解释器内存够大,但是当前大多数浏览器都不能支持2^28(即268435456)位字符串。如果全部存储英文字符,则大小为268435456 / 1024 / 1024 = 256 MB

那么这么大的字符串在内存中是怎么存储的呢?
ECMAScript 5.1规范在“内存”的定义上非常模糊,并没有明确定义出ECMAScript实现中的各个运行时区域的划分,也就不存在“stack/heap划分”。所以,从定义层面看不出String存储的字符串是在stack还是heap上分配的。

ECMAScript规范只规定了String类型要是一个值类型,这个类型要可以存储UTF-16为单元的字符,完全没有规定String类型要如何实现。所以各个JavaScript引擎的具体实现就各显神通了。

V8在内存分配上面,对String直接使用堆存储,不用经过c++堆外分配内存,并且Google也对String进行优化,在实际的拼接测速对比中,String比Buffer快。

在 V8 中字符串有如下 5 种表达模式:
SeqString
已经要查看内容的字符串:使用flat string思路来实现,本质上说就是用数组形式来存储String的内容。
实际数据存储时分为 OneByte、TwoByte(Unicode)两类。

ConsString
拼接字符串但尚未查看其内容:在字符串拼接时,采用树形结构表达拼接后(first + second)的字符串。使用“rope”思路或其它延迟拼接的思路来实现。当需要查看其内容时则进行“扁平化”操作将其转换为flat string表现形式。最常见rope的内部节点就像二叉树一样,但也可以有采用更多叉树的设计的节点,或者是用更动态的多叉树实现。

SliceString(parent, offset)
子串(substring):在字符串切割时,采用 offset 与 [length] 表达父字符串(parent)的一部分。使用“slice”思路来实现,也就是说它只是一个view,自己并不存储字符内容而只是记录个offset和length,底下的存储共享自其引用的源字符串。

ThinString(actual)
值得驻留(intern)的字符串:在有些场景下会重复出现的字符串,当两个变量保存相同的字符串时,它们实际上是保存了这个字符串在内存中的地址。最大的好处是在特殊场景下有些字符串会经常重复出现,或者要经常用于相等性比较,把这些字符串驻留起来可以节省内存(内容相同的字符串只驻留一份),并且后续使用可以使用指针比较来代替完全的相等性比较(因为驻留的时候已经比较过了)。在多数情况下可以被认为与 ConsString(actual, empty_string) 等价。

ExternalString
外来字符串:代表了产生在 V8 堆外的字符串资源。有时候JavaScript引擎跟外界交互,外界想直接把一个char8_t或者char16_t传给JavaScript引擎当作JavaScript字符串用。JavaScript引擎可能会针对某些特殊场景提供一种包装方式来直接把这些外部传进来的字符串当作JavaScript String,而不拷贝其内容。

值得注意的是:虽然ECMAScript的String值是值类型的,这并不就是说“String值就是在栈上的”。正好相反,V8所实现的String值全部都是在V8的GC堆上存储的,传递String值时实际上传递的是指向它的指针。但由于JavaScript的String值是不可变的,所以底层实现无论是真的把String“放在栈上”还是传递指针,对上层应用的JavaScript代码而言都没有区别。

ExternalString虽然特殊但也不例外:它实际存储字符串内容的空间虽然是从外部传进来的,不在V8的GC堆里,但是ExternalString对象自身作为一个对象头还是在GC堆里的,所以该String类型实现逻辑上说还是在GC堆里。

v8中,Boolean、Null、Undefined、Number保存在栈内存中,这些类型的值都可以用32位的数据来表示。那么剩下的数据类型Symbol、BigInt、Object、String就都是要保存在堆中的,栈里面只会保存这些值的地址的引用。

栈内存:

1.存储的值大小固定
2.空间较小
3.可以直接操作其保存的变量,运行效率高
4.由系统自动分配存储空间

栈内存是一个线性的、规则的、大小基本固定的、有序的排列起来的一块块内存空间,每个单元大小固定,规则有序的排列下来

堆内存:

5.存储的值大小不定,可动态调整
6.空间较大,运行效率低
7.无法直接操作其内部存储,使用引用地址读取
8.通过代码进行分配空间


数字

Number,遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 位,其中1位用来表示符号位,11位用来表示指数,剩下52位表示尾数,就是这个Number.MAX_SAFE_INTEGER,又称为最大安全数,其值为9007199254740991,换算成二进制就是2^53-1,即占52位。。大于 9007199254740992 的可能会丢失精度

9007199254740992 + 1 // 丢失
9007199254740992 + 2 // 未丢失
9007199254740992 + 3 // 丢失
9007199254740992 + 4 // 未丢失

类似的,数字还有一个最小安全数Number.MIN_SAFE_INTEGER,其值为-9007199254740991

数字最大数为Number.MAX_VALUE,其值为1.7976931348623157e+308,介于2^1023 – 2^1024之间。大于的值MAX_VALUE表示为Infinity

ECMAScript 标准约定number数字需要被当成 64 位双精度浮点数处理,但事实上,一直使用 64 位去存储任何数字实际是非常低效的,所以 JavaScript 引擎并不总会使用 64 位去存储数字,引擎在内部采用其他内存表示方式(如 32 位),只要保证数字外部所有能被监测到的特性对齐 64 位的表现就行。

V8不仅仅是使用32位来表示数字那么简单,还对数字进行了分类,将数字分为了Smi 和 HeapNumber(这个仅仅是引擎层面的处理,js内部只认识数字,不区分整数和浮点数)。

V8中Smi代表的是小整数(-2^31,2^31-1),而HeapNumber则代表了一些浮点数以及无法用32位表示的数,比如NaN,Infinity,-0等。因为小整数在我们的编码过程中太常见了,所以,V8专门把它拿出来,并且对其进行了优化操作,这样它就可以进行快速整型操作,比如for循环等。

当我们更新他们的值的时候,Smi的值会原地更新,而HeapNumber由于它不可变的特性,V8会开辟一个新的内存实体用来储存新的值,如果我们需要频繁更新HeapNumber的值,执行效率会比Smi慢得多。


v8引擎与内存分配

V8 是谷歌开发的高性能 JavaScript 引擎,该引擎使用 C++ 开发。目前主要应用在 Google Chrome 浏览器和 node.js 当中。

V8 自带的高性能垃圾回收机制,使开发者能够专注于程序开发中,极大的提高开发者的编程效率。但是方便之余,也会出现一些对新手来说比较棘手的问题:进程内存暴涨,cpu 飙升,性能很差等。

一个 V8 进程的内存通常由以下几个块构成:

新生代内存区(new space)

64位下新生代的空间为64M,32位下新生代为16M。大多数的对象都会被分配在这里,这个区域很小但是垃圾回收比较频繁;

老生代内存区(old space)

64位下老生代为1400M;32位下老生代为700M。属于老生代,这里只保存原始数据对象,这些对象没有指向其他对象的指针;

大对象区(large object space)

这里存放体积超越其他区大小的对象,每个对象有自己的内存,垃圾回收其不会移动大对象区;

代码区(code space)

代码对象,会被分配在这里。唯一拥有执行权限的内存;

map 区(map space)

存放 Cell 和 Map,每个区域都是存放相同大小的元素,结构简单。

来自:https://blog.csdn.net/hf872914334/article/details/107383643/

链接: https://fly63.com/article/detial/9474

JavaScript 内存管理和垃圾回收

JavaScript 的内存管理和垃圾回收,是个略生僻的话题,因为在JavaScript 中不显式执行内存操作,不过最好了解它如何工作。

js常见的内存泄漏及解决方法总汇

js具有自动回收垃圾的机制,即执行环境会负责管理程序执行中使用的内存。在C和C++等其他语言中,开发者的需要手动跟踪管理内存的使用情况。在编写js代码时候,开发人员不用再关心内存使用的问题,所需内存的分配 以及无用的回收完全实现了自动管理。

浅谈javaScript内存机制

javaScript内存空间并不是一个经常被提及的概念,想要对JS的理解更加深刻,就必须对内存空间有一个清晰的认:栈与堆、复杂数据类型与基本数据类型、引用数据类型与堆内存

js 把一个对象赋值给另一个对象会指向同一个内存地址

实际上并不是新建一个和原对象(数组也是对象)完全一样的对象,而是把原对象的内存地址直接复制给了另一个对象,也就是说两个对象都是指向同一个内存地址,所以实际上它们就是同一个对象。

php底层原理之垃圾回收机制

php垃圾回收机制,对于PHPer来说是一个不陌生但是又不是很熟悉的内容。那么php是怎么实现对不需要的内存进行回收的呢?首先还是需要了解下基础知识,便于垃圾回收原理内容的理解。

js变量、作用域和内存问题

JavaScript变量可以用来保存两种类型的值:基本类性值和引用类性值。所有变量(包括基本类型和引用类型)都存在于一个执行环境(也称为作用域)当中,具有自动垃圾收集机制的编程语言,开发人员不必关心内存分配和回收问题

php中的内存管理

计算机的内存由操作系统进行管理,所以普通应用程序是无法直接对内存进行访问的。应用程序只能向操作系统申请内存,通常的应用也是这么做的,在需要的时候通过类似malloc之类的库函数 向操作系统申请内存。

原生JS与Jquery删除iframe并释放内存-IE

当项目以tab页签方式打开多个iframe窗口时,关闭tab页签同时也需要关闭iframe并释放内存资源,主要是针对IE浏览器。

闭包真的会导致内存泄漏?

今天遇到一个很有争议的问题,在这里分享一下,我相信对于即将面试前端的小伙伴会有帮助的。主要内容是围绕下边的问题展开的,文章涉及到的其他方面的知识点不展开叙述。

Web 应用的内存优化

随着 Web 应用复杂程度越来越高,以及 NodeJS 大规模投入生产环境,许多 Web 应用都会长时间运行, JavaScript 的内存管理显得更为重要。JavaScript 具备自动回收垃圾的机制

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!