【进阶1-4期】JavaScript深入之带你走进内存机制(转)
这是我在公众号(高级前端进阶)看到的文章,现在做笔记
https://mp.weixin.qq.com/s/yK4DPKhkmkiroasWJMrJcw
阅读笔记
JS内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。 其中栈存放变量,堆存放复杂对象,池存放常量,所以也叫常量池。
昨天文章介绍了堆和栈,小结一下,【进阶1-3期】JavaScript深入之内存空间详细图解
基本类型:-->
栈内存(不包含闭包中的变量)引用类型:-->
堆内存
今日补充一个知识点,就是闭包中的变量并不保存中栈内存中,而是保存在堆内存中,这也就解释了函数之后之后为什么闭包还能引用到函数内的变量。
function A() {
let a =
function B() {
console.log(a)
}
return B
}
闭包的简单定义是:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。
函数 A 弹出调用栈后,函数 A 中的变量这时候是存储在堆上的,所以函数B依旧能引用到函数A中的变量。现在的 JS 引擎可以通过逃逸分析辨别出哪些变量需要存储在堆上,哪些需要存储在栈上。
闭包的介绍点到为止,【进阶2期】 作用域闭包会详细介绍,敬请期待。
今天文章的重点是内存回收和内存泄漏。
内存回收
JavaScript有自动垃圾收集机制,垃圾收集器会每隔固定的时间段就执行一次释放操作,找出那些不再继续使用的值,然后释放其占用的内存。
局部变量和全局变量的销毁
局部变量:局部作用域中,当函数执行完毕,局部变量也就没有存在的必要了,因此垃圾收集器很容易做出判断并回收。
全局变量:全局变量什么时候需要自动释放内存空间则很难判断,所以在开发中尽量避免使用全局变量。
以Google的V8引擎为例,V8引擎中所有的JS对象都是通过堆来进行内存分配的
初始分配:当声明变量并赋值时,V8引擎就会在堆内存中分配给这个变量。
继续申请:当已申请的内存不足以存储这个变量时,V8引擎就会继续申请内存,直到堆的大小达到了V8引擎的内存上限为止。
V8引擎对堆内存中的JS对象进行分代管理
新生代:存活周期较短的JS对象,如临时变量、字符串等。
老生代:经过多次垃圾回收仍然存活,存活周期较长的对象,如主控制器、服务器对象等。
垃圾回收算法
对垃圾回收算法来说,核心思想就是如何判断内存已经不再使用,常用垃圾回收算法有下面两种。
引用计数(现代浏览器不再使用)
标记清除(常用)
引用计数
引用计数算法定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。如果没有其他对象指向它了,说明该对象已经不再需要了。
// 创建一个对象person,他有两个指向属性age和name的引用
var person = {
age: ,
name: 'aaaa'
}; person.name = null; // 虽然name设置为null,但因为person对象还有指向name的引用,因此name不会回收 var p = person;
person = ; //原来的person对象被赋值为1,但因为有新引用p指向原person对象,因此它不会被回收
引用计数有一个致命的问题,那就是循环引用
如果两个对象相互引用,尽管他们已不再使用,但是垃圾回收器不会进行回收,最终可能会导致内存泄露。
function cycle() {
var o1 = {};
var o2 = {};
o1.a = o2;
o2.a = o1;
return "cycle reference!"
}
cycle();
cycle函数执行完成之后,对象o1和o2实际上已经不再需要了,但根据引用计数的原则,他们之间的相互引用依然存在,因此这部分内存不会被回收。所以现代浏览器不再使用这个算法。
但是IE依旧使用。
var div = document.createElement("div");
div.onclick = function() {
console.log("click");
};
上面的写法很常见,但是上面的例子就是一个循环引用。
变量div有事件处理函数的引用,同时事件处理函数也有div的引用,因为div变量可在函数内被访问,所以循环引用就出现了。
标记清除(常用)
标记清除算法将“不再使用的对象”定义为“无法到达的对象”。即从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,保留。那些从根部出发无法触及到的对象被标记为不再使用,稍后进行回收。
无法触及的对象包含了没有引用的对象这个概念,但反之未必成立。
所以上面的例子就可以正确被垃圾回收处理了。
所以现在对于主流浏览器来说,只需要切断需要回收的对象与根部的联系。最常见的内存泄露一般都与DOM元素绑定有关:
email.message = document.createElement(“div”);
displayList.appendChild(email.message); // 稍后从displayList中清除DOM元素
displayList.removeAllChildren();
上面代码中,div元素已经从DOM树中清除,但是该div元素还绑定在email对象中,所以如果email对象存在,那么该div元素就会一直保存在内存中。
内存泄漏
对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。 对于不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)
内存泄漏识别方法
1、浏览器方法
打开开发者工具,选择 Memory
在右侧的Select profiling type字段里面勾选 timeline
点击左上角的录制按钮。
在页面上进行各种操作,模拟用户的使用情况。
一段时间后,点击左上角的 stop 按钮,面板上就会显示这段时间的内存占用情况。
2、命令行方法
使用Node提供的process.memoryUsage方法。
console.log(process.memoryUsage()); // 输出
{
rss: , // resident set size,所有内存占用,包括指令区和堆栈
heapTotal: , // "堆"占用的内存,包括用到的和没用到的
heapUsed: , // 用到的堆的部分
external: // V8 引擎内部的 C++ 对象占用的内存
}
判断内存泄漏,以heapUsed字段为准。
详细的JS内存分析将在【进阶20期】性能优化详细介绍,敬请期待。
WeakMap
ES6 新出的两种数据结构:WeakSet 和 WeakMap,表示这是弱引用,它们对于值的引用都是不计入垃圾回收机制的。
const wm = new WeakMap();
const element = document.getElementById('example'); wm.set(element, 'some information');
wm.get(element) // "some information"
先新建一个 Weakmap 实例,然后将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在 WeakMap 里面。这时,WeakMap 里面对element的引用就是弱引用,不会被计入垃圾回收机制。
【进阶1-4期】JavaScript深入之带你走进内存机制(转)的更多相关文章
- JavaScript学习笔记 -- 带参数arguments的函数的用法
JavaScript函数有带参数与不带参数两种形式,不带参数情况如下: function myFunction() { alert('HelloWorld!') } 在这种类型的函数中,输出值是确定的 ...
- amazeui学习笔记二(进阶开发4)--JavaScript规范Rules
amazeui学习笔记二(进阶开发4)--JavaScript规范Rules 一.总结 1.注释规范总原则: As short as possible(如无必要,勿增注释):尽量提高代码本身的清晰性. ...
- 前端进阶必读:《JavaScript核心技术开发解密》核心提炼二
前言 最近读勒基本关于前端的数据<JavaScript核心技术开发解密>,<webpack从入门到进阶>...这几本书帮助到我更好的理解JS.webpack在前端技术领域中的作 ...
- JavaScript基本知识点——带你逐步解开JS的神秘面纱
JavaScript基本知识点--带你逐步解开JS的神秘面纱 在我们前面的文章中已经深入学了HTML和CSS,在网页设计中我们已经有能力完成一个美观的网页框架 但仅仅是网页框架不足以展现出网页的魅力, ...
- JavaScript学习系列6 -- JavaScript中的垃圾回收(内存释放)
程序开发中,涉及到的内存生命周期基本是一样的,分为以下三步 1. 分配需要的内存 2. 使用分配到的内存 3. 释放其内存 ----什么时候释放内存,以及需要释放哪些变量的内存, 就是垃圾回收机 ...
- MYSQL(基本篇)——一篇文章带你走进MYSQL的奇妙世界
MYSQL(基本篇)--一篇文章带你走进MYSQL的奇妙世界 MYSQL算是我们程序员必不可少的一份求职工具了 无论在什么岗位,我们都可以看到应聘要求上所书写的"精通MYSQL等数据库及优化 ...
- 小丁带你走进git的世界三-撤销修改
一.撤销指令 git checkout还原工作区的功能 git reset 还原暂存区的功能 git clean 还没有被添加进暂存区的文件也就是git还没有跟踪的文件可以使用这个命令清除他们 g ...
- 小丁带你走进git的世界二-工作区暂存区分支
小丁带你走进git的世界二-工作区暂存区分支 一.Git基本工作流程 1.初始化一个仓库 git init git clone git仓库分为两种情况: 第一种是在现有项目或目录下导入所有文件到 ...
- 小丁带你走进git世界一-git简单配置
小丁带你走进git世界一-git简单配置 1.github的简单配置 配置提交代码的信息,例如是谁提交的代码之类的. git config –global user.name BattleHeaer ...
随机推荐
- FontAwesome图标选择器
虽然比较简单,但很实用,在C# MVC下开发,请首先将图标名称保存到数据库中,图标窗口如下. @{ Layout = "~/Views/Shared/_Form.cshtml"; ...
- IP白名单
一.什么是IP白名单 公众平台后台新增了IP白名单功能.通过开发者ID及密码调用获取access_token接口时,需要设置访问来源IP为白名单. IP白名单是指一组IP列表,只有该列表中的IP地址的 ...
- None.js 第六步 Stream(流)
输出流 var fs = require("fs"); var data = ''; // 创建可读流 var readerStream = fs.createReadStream ...
- 向JSP中静态导入HTML文件时,运行jsp时,html中中文产生乱码问题最简单的解决方法
在保证其他的编码格式一致的情况下 在html标签内,head标签外添加一下代码时可以完美解决 亲测 <%@page pageEncoding="UTF-8"%>
- Servlet 起航 文件上传 中文文件名下载
@WebServlet(name = "ticketServlet",urlPatterns = {"/tickets"},loadOnStartup = 1) ...
- 使用PHP连接数据库实现留言板功能
PHP实现留言板功能: 1 首先是登录页面: <!DOCTYPE html><html> <head> <meta charset=&qu ...
- PHP文件缓存包含三种格式
http://developer.51cto.com/art/200912/166975.htm PHP文件缓存的速度一直是PHP程序员们关心的问题,他们一直在探讨着如何才能提高PHP文件缓存的效率来 ...
- 迅为4412开发板QtE系统源码-屏幕横竖屏切换修改方法
迅为4412开发板QtE系统源码-屏幕横竖屏切换修改方法 详情了解:http://topeetboard.com 更多了解:https://arm-board.taobao.com/ 用户在开发板上运 ...
- :target方法实现切换
<!DOCTYPE HTML><html><head> <title>:target切换</title> <meta charset= ...
- mui的switch开关的应用
HTML: <!--mui的switch开关--> <div class="mui-content-padded"> <h5>switch开关m ...