IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 【JS】内存管理 -> 正文阅读

[JavaScript知识库]【JS】内存管理

内存生命周期

  • 不管什么程序语言,内存生命周期基本是一致的:
1. 分配你所需的内存
2. 使用分配到的内存(比如进行读写操作)
3. 不需要时将其归还、释放

所有语言第二部分都是明确的。第一和第三部分在底层语言中是明确的,但在像JavaScript这些高级语言中,大部分都是隐含的。
JavaScript是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时“自动”释放。 释放的过程称为垃圾回收。这个“自动”是混乱的根源,并让JavaScript(和其他高级语言)开发者错误的感觉他们可以不关心内存管理。

内存分配

  • 为了不让程序员费心分配内存,JavaScript 在定义变量时就完成了内存分配。
var n = 123; // 给数值变量分配内存
var s = "azerty"; // 给字符串分配内存

var o = {
  a: 1,
  b: null
}; // 给对象及其包含的值分配内存

// 给数组及其包含的值分配内存(就像对象一样)
var a = [1, null, "abra"];

function f(a){
  return a + 2;
} // 给函数(可调用的对象)分配内存

// 函数表达式也能分配一个对象
someElement.addEventListener('click', function(){
  someElement.style.backgroundColor = 'blue';
}, false);
  • 有些函数调用结果是分配对象内存:
var d = new Date(); // 分配一个 Date 对象

var e = document.createElement('div'); // 分配一个 DOM 元素
  • 有些方法分配新变量或者新对象:
var s = "azerty";
var s2 = s.substr(0, 3); // s2 是一个新的字符串
// 因为字符串是不变量,
// JavaScript 可能决定不分配内存,
// 只是存储了 [0-3] 的范围。

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2);
// 新数组有四个元素,是 a 连接 a2 的结果

使用值

使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。

当内存不再需要使用时释放

高级语言解释器嵌入了“垃圾回收器”,它的主要工作是跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。这只能是一个近似的过程,因为要知道是否仍然需要某块内存是无法判定的(无法通过某种算法解决)。

  • 大多数内存管理的问题都在这个阶段。
  • 在这里最艰难的任务是找到“哪些被分配的内存确实已经不再需要了”。
  • 它往往要求开发人员来确定在程序中哪一块内存不再需要并且释放它。

内存泄漏

内存泄露概念

在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。 ——维基百科

  • 程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存
  • 对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。
  • 否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。
  • 不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。

内存泄露案例

全局变量:

  • 案例 1 : 在 JS 中处理未被声明的变量
function fun() {
    text = "文本";
}
上述范例中的 text 时, 会把 text , 定义到全局对象中, 在浏览器中就是 window 上. 
在页面中的全局变量, 只有当页面被关闭后才会被销毁. 所以这种写法就会造成内存泄露, 
当然在这个例子中泄露的只是一个简单的字符串, 但是在实际的代码中, 往往情况会更加糟糕.
  • 案例 2:意外创建全局变量
function fun() {
    this.text = "文本";
}
// fun 被调用时, this 指向全局变量(window)
fun();
在这种情况下调用 foo, this被指向了全局变量 window, 意外的创建了全局变量.
  • 总结:
我们谈到了一些意外情况下定义的全局变量, 代码中也有一些我们明确定义的全局变量. 
如果使用这些全局变量用来暂存大量的数据, 记得在使用后, 对其重新赋值为 null.

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

  • 在很多库中, 如果使用了观察着模式, 都会提供回调方法, 来调用一些回调函数. 要记得回收这些回调函数.
var serverData = loadData();
setInterval(function() {
    var renderer = document.getElementById('renderer');
    if(renderer) {
        renderer.innerHTML = JSON.stringify(serverData);
    }
}, 5000); // 每 5 秒调用一次
如果后续 renderer 元素被移除, 整个定时器实际上没有任何作用. 
但如果你没有回收定时器, 整个定时器依然有效, 
不但定时器无法被内存回收, 定时器函数中的依赖也无法回收. 
在这个案例中的 serverData 也无法被回收.

闭包

  • 闭包:一个内部函数, 有权访问包含其的外部函数中的变量
var theThing = null;
var replaceThing = function () {
  var originalThing = theThing;
  var unused = function () {
    if (originalThing) // 对于 'originalThing'的引用
      console.log("hi");
  };
  theThing = {
    longStr: new Array(1000000).join('*'),
    someMethod: function () {
      console.log("message");
    }
  };
};
setInterval(replaceThing, 1000);
这段代码, 每次调用 replaceThing 时:
theThing 获得了包含一个巨大的数组和一个对于新闭包 someMethod 的对象. 
同时 unused 是一个引用了 originalThing 的闭包。

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

DOM 引用

  • 很多时候, 我们对 Dom 的操作, 会把 Dom 的引用保存在一个数组或者 Map 中
var elements = {
    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 元素的引用, 依然无法对齐进行内存回收.

内存泄漏识别

  • Chrome 浏览器查看内存占用,按照以下步骤操作
1. 打开开发者工具,选择 Timeline 面板
2. 在顶部的Capture字段里面勾选 Memory
3. 点击左上角的录制按钮。
4. 在页面上进行各种操作,模拟用户的使用情况。
5. 一段时间后,点击对话框的 stop 按钮,面板上就会显示这段时间的内存占用情况。
  • 老版的图
    在这里插入图片描述
  • 新版图如下:

在这里插入图片描述

  • 如果内存占用基本平稳,接近水平,就说明不存在内存泄漏。

在这里插入图片描述

  • 反之,就是内存泄漏了。

在这里插入图片描述

  • 命令行可以使用 Node 提供的process.memoryUsage方法。
console.log(process.memoryUsage());
// { rss: 27709440,
//  heapTotal: 5685248,
//  heapUsed: 3449392,
//  external: 8772 }
  • 判断内存泄漏,以heapUsed字段为准。
rss(resident set size):所有内存占用,包括指令区和堆栈。
heapTotal:"堆"占用的内存,包括用到的和没用到的。
heapUsed:用到的堆的部分。
external: V8 引擎内部的 C++ 对象占用的内存。
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-07 11:56:25  更:2021-12-07 11:57:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:51:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码