首页 > 系统相关 >内存泄漏(前端)

内存泄漏(前端)

时间:2023-08-23 11:34:58浏览次数:29  
标签:function 泄漏 console 作用域 前端 内存 div document

内存泄漏

这是我在部门做的内部分享,收到大家很好地反馈,现分享出来

理论

概念

  • 程序在申请内存后,无法释放已申请的内存空间

与内存溢出的关系

  • 内存溢出是程序在申请内存时,没有足够的内存空间供其使用
  • 是内存泄漏的最终结果

垃圾回收(GC)

内存资源是有限的,所以需要适当的时机释放合适的资源,因此就出现了垃圾回收(Garbage Collection)

"垃圾"如何认定
  • 引用计数(Reference Counting):引擎检测一个对象是否有其他对象引用,如果引用为0则释放,反之不释放
  • 可达性分析(GC Roots Tracing):从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
怎么回收“垃圾”
  • 标记-清除(Mark-Sweep):标记无用对象,然后进行清除回收

  • 复制收集(Copy and Collection):按照内存容量划分为两块大小相等的区域,当一块用完的时候将“活着”的对象复制到另一块上去,然后清除已沾满块的内存

  • 标记-整理(Mark-Compact):标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存

  • 分代收集(Generational Collecting):根据对象存货周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记-整理算法

为什么在SPA下内存泄漏的出现的如此频繁?

CSR(Client-Side Rendering)vs SSR(Server-Side Rendering)

  • CSR, 使用JavaScript渲染页面,用户体验较好
  • SSR, 页面再服务器端渲染好后发送到浏览器,渲染速度快

前端越来越“重”(大势所趋)

内存管理

内存模型

栈是一块按后进先出规则访问的存储区域,主要存放程序运行时函数的参数与局部变量等

堆表示一块存储区域,对该存储区域的访问是任意的,没有后进先出的要求,主要用于在程序运行时动态分配内存,比如new一个新的对象,就是在堆中分配存储空间的,一般由程序员手动控制,但也容易造成内存泄漏

如何释放内存

堆内存释放
var obj = {p1: 'hi'}

obj = null // 释放
栈内存释放
  • 全局作用域

在全局作用域下,只有当页面关闭的时候,全局作用域才会被销毁。

  • 私有作用域

一般来说,私有作用域下的代码执行完后,会主动销毁和释放。
但有一些特殊的情况:当私有域中有部分内容被之外的地方引用,那么当前作用域就不会销毁

常见的内存泄漏

全局变量
function createGlobalVariables() {
    // 没有声明从而制造了隐式全局变量leaking1
    leaking1 = 1000;

    // 函数内部this指向window,制造了隐式全局变量leaking1
    this.leaking2 = 2000;
}

createGlobalVariables()

console.log(window.leaking1) // 1000
console.log(window.leaking2) // 2000
如何避免:使用严格模式(“use Strict”),来消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
闭包
function closure() {
    var num = 100;
    return function () {
        num++;
        console.log(num);
    }
}

var fn = closure(); // closure执行形成的作用域就不能再销毁了

// fn = null
如何避免:闭包是JS中重要的部分,因此怎么“合理”使用闭包至关重要,比如在什么时候创建闭包?保留了哪些对象?
脱离(Detached)的DOM元素
function createElement() {
    const div = document.createElement('div');
    div.id = 'detached';
    return div;
}

// 这将继续对‘div’的引用,即使调用了deleteElement()
const detachedDiv = createElement();

document.body.appendChild(detachedDiv);

function deleteElement() {
    document.body.removeChild(document.getElementById('detached'));
}

deleteElement();
如何避免:将对DOM的引用移到局部作用域
function createElement() {
    const div = document.createElement('div');
    div.id = 'detached';
    return div;
}

// 将对DOM的引用移到局部作用域
function appendElement() {
    const detachedDiv = createElement();
    document.body.appendChild(detachedDiv);
}

appendElement();

function deleteElement() {
    document.body.removeChild(document.getElementById('detached'));
}

deleteElement();
事件监听器
const hugeString = new Array(100000).join('x');

document.addEventListener('keyup', function () {
    doSomething(hugeString); // hugeString 会一直保留不会被GC回收
});
如何避免:一旦不再需要事件监听器,记得调用removeEventListener方法来注销事件监听器
观察者模式
  • eventBus
计时器
  • setInterval
如何避免:获取计时器的引用,在“合适”的时机清理掉计时器
强引用对象

Map, Set

let addrs = {city: '北京', district: '海淀'};
let student = {name: '小强', address: addrs}
let myset = new Set([student])

student = null
console.log(myset)
如何避免:使用WeakMap, WeakSet(ES6)
let addrs = {city: '北京', district: '海淀'};
let student = {name: '小强', address: addrs}
let weakSet = new WeakSet([student])
// weakSet.add(student)

student = null

setInterval(() => {
    console.log('===', weakSet)
}, 5000)
Console

浏览器会记录console里对象引用,因此console参数为对象的话,也会造成不释放

如何避免:在build阶段清除console.log相关代码

实战

怎么排查?

工欲善其事必先利其器

Performance + Memory

Memory
  • Summary(摘要) view就是当前内存快照的一个概览

Summary view

  • Comparison(比较)view可以让你对比两份内存快照之间的差异

Comparison view

  • Containment(包含) view提供了一个自下而上的视图,用来分析一些全部变量的引用情况

Containment view

  • Statistics(统计) view用饼图的形式展示各个类型对象的内存占比

Containment view

Tips

  1. 使用浏览器无痕模式
  2. 尽量使用没有混淆的代码
  3. 排查问题时使用线上的代码
  4. 在现场打内存快照,便于跳转到源代码所在行
  5. Retained Size > Shallow Size
  6. Maximum Count of Objects

方法

  • "3 snapshot" (2012)

  • 我的方法

    一、看Performance面板,打开记录一段时间,看内存增长曲线是否有异常

    二、看Memory面板,选择“Allocation sampling(分配采样)”开始记录

    三、多操作几遍

    四、回到Memory面板,查看“Chart”找出红/黄色区域,点进去查看代码是否有问题

项目案例

addEventListener

  • 回调函数执行几次?

vue directives

  • DOM移除后,已注册的事件也会随着销毁吗?

setInterval

  • clear

keep-alive

  • max

终极杀器

  • window.gc

标签:function,泄漏,console,作用域,前端,内存,div,document
From: https://www.cnblogs.com/Khadron/p/17650746.html

相关文章

  • 前端好用API之MutationObserver
    https://www.cnblogs.com/xwwin/p/16587930.html  前情一直以来都没有好的方式可以监听元素变化,Mutationevents虽然可以监听DOM树结构变化,但是因性能问题和差的兼容问题(Webkit内核不支持)并不推荐使用。MutationObserver介绍MutationObserver接口提供了监视对DOM树所做更......
  • RDMA远程直接内存访问
    RDMA(RemoteDirectMemoryAccess)技术全称远程直接内存访问,就是为了解决网络传输中服务器端数据处理的延迟而产生的。它将数据直接从一台计算机的内存传输到另一台计算机,无需双方操作系统的介入。这允许高吞吐、低延迟的网络通信,尤其适合在大规模并行计算机集群中使用。RDMA通过网......
  • 【前端基础总结】
    Web前端参考手册【前端引入】【HTML】【1】web服务端本质软件开发架构HTTP协议HTML简介HTML注释语法HTML文档结构HTML标签分类head常用标签body常用标签块级标签/行内标签【2】HTML补充表格标签form表单【CSS】【3】CSSCSS简介注释语法语法结构引入方式......
  • 前端codeReview规范指南
    一定要看的前端codeReview规范指南 一、前言针对目录结构、CSS规范、JavaScript规范、Vue规范可参照官方给出的 风格指南这里主要总结业务开发中常遇到的代码问题和实践,帮助大家后续各自做好codeReview,一些你遇到的典型问题,也可以在留言区评论,帮助团队共同进步。二、实践规......
  • C语言数组(3)--- 一维数组的内存存储
    一.引入我们前面已经介绍了一维数组的创建以及使用,下面我们来探究一下一维数组在内存中的存储#define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>intmain(void){ intarr[]={1,2,3,4,5,6,7,8,9,10}; intsz=sizeof(arr)/sizeof(arr[0]); for(inti=0;i<sz;i++......
  • 02.前后端分离中台框架前端 admin.ui.plus 学习-介绍与简单使用
    中台框架前台项目admin.ui.plus的初识基于vue3.x+CompositionAPIsetup语法糖+typescript+vite+elementplus+vue-router-next+pinia技术,内置支持一键生成微服务接口,适配手机、平板、pc的后台权限管理框架,希望减少工作量,帮助大家实现快速开发。框架一览......
  • 源支付5.1.7前端+后台+云端协议2.0打造更专业的聚合免签支付系统
    下载资料自取:  提取码:2jmv推荐系统为:CentOS7.6Linux系统环境:Nginx1.20.1+MySQL5.6.50+PHP-7.2+Redis将商户后台源码上传运行目录为Public伪静态为thinkphp访问域名傻瓜模式安装sudorpm-Uvhhttps://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm......
  • 源支付5.1.7前端+后台+云端协议2.0打造更专业的聚合免签支付系统
    推荐系统为:CentOS7.6Linux系统环境:Nginx1.20.1+MySQL5.6.50+PHP-7.2+Redis将商户后台源码上传运行目录为Public伪静态为thinkphp访问域名傻瓜模式安装sudorpm-Uvhhttps://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm完成后输入:sudoyuminstalld......
  • OS(七):存储器管理之内存管理方式
    1、连续分配方式连续分配方式:为用户程序分配一个连续的内存空间。连续分配有4种方式,分别为单一连续分配、固定分区分配、动态分区分配及动态重定位分配。1.1、单一连续分配作用与单用户、单任务操作系统。内存被分为系统区和用户区,系统区供OS使用,通常放在......
  • SQL Server因设置最大内存太小导致无法启动的解决方案
    首先是    在服务器上发现SQLServer占用内存过大,128G服务器内存它占用高达100多G。于是就去找解决方案,找了几篇文章发现都是通过修改SQLServer服务器最大内存让其释放占用内存,如图所示,我把最大内存修改为128MB。发现问题    然后我就发现我的SQLServer服务无......