图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)

简介: 图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)

说明

图解 Google V8 学习笔记



几种内存问题


内存问题可以定义为三类:


  • 内存泄漏 (Memory leak):导致页面的性能越来越差;
  • 内存膨胀 (Memory bloat):导致页面的性能会一直很差;
  • 频繁垃圾回收:导致页面出现延迟或者经常暂停。




内存泄漏


内存泄漏:当进程不再需要某些内存的时候,这些不再被需要的内存依然没有被进程回收。


例子1:使用未定义的变量

function foo() {
    temp_array = new Array(200000)
}



当执行这段代码时,由于函数体内的对象没有被 var、let、const 这些关键字声明,那么 V8 就会使用 this.temp_array 替换 temp_array

function foo() {
    this.temp_array = new Array(200000)
}



这里的 this 指向常驻内存 的 window 对象,即便 foo 函数退出了,依然被 window 对象引用,这就造成了 temp_array 的泄漏。


为了解决这个问题,可以在 JavaScript 文件头部加上 use strict;,使用严格模式避免意外的全局变量。


没有加 use strict;,this 指向 window 对象。


719915441fc744b08718b3282ae1102a.png

加上 use strict;,this 指向 undefined。


2e3ac846bce044e7b685f0d8a7c4ab41.png



例子2:使用闭包

因为闭包会引用父级函数中定义的变量,如果引用了不被需要的变量,那么也会造成内存泄漏。

function foo(){  
    var temp_object = new Object()
    temp_object.x = 1
    temp_object.y = 2
    temp_object.array = new Array(200000)
    return function(){
        console.log(temp_object.x);
    }
}


那么当调用完 foo 函数之后,由于返回的匿名函数引用了 foo 函数中的 temp_object.x,这会造成 temp_object 无法被销毁,即便只是引用了 temp_object.x,也会造成整个 temp_object 对象依然保留在内存中。

88a04e253be44143918dd145799e32b6.png


从上图可以看出,我们仅仅是需要 temp_object.x 的值,V8 却保留了整个 temp_object 对象。

怎么解决这个问题?


我们可以根据实际情况,来判断闭包中返回的函数到底需要引用什么数据,不需要引用的数据就绝不引用。


可以改成下面的方式:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>        
        function foo(){  
            var temp_object = new Object()
            temp_object.x = 1
            temp_object.y = 2
            temp_object.array = new Array(200000)
            let kaimo313 = temp_object.x;
            return function(){
                console.log(kaimo313);
                debugger
            }
        }
        foo()();
    </script>
</body>
</html>


05a2d59d1ab04203898115840d967c15.png

可以看到闭包引用的仅仅是一个 kaimo313 的变量。



例子3:DOM 内存泄漏


如果某个节点已从 DOM 树移除,但 JavaScript 仍然引用它,我们称此节点为 detached。detached 节点是 DOM 内存泄漏的常见原因。

let detachedTree;
function create() {
    var ul = document.createElement('ul');
    for (var i = 0; i < 100; i++) {
        var li = document.createElement('li');
        ul.appendChild(li);
    }
    detachedTree = ul;
}
create()


由于 JavaScript 代码中保留了这些元素的引用,导致这些 DOM 元素依然会呆在内存中,这些 DOM 元素从 DOM 上被移除后,它们并不会立即销毁。



内存膨胀


内存膨胀和内存泄漏有一些差异,内存膨胀主要表现在程序员对内存管理的不科学,额外使用过多的内存有可能是没有充分地利用好缓存,也有可能加载了一些不必要的资源。通常表现为内存在某一段时间内快速增长,然后达到一个平稳的峰值继续运行。


比如:只需要 50M 内存就可以搞定的,有些程序员却花费了 500M 内存。


内存膨胀和内存泄漏的关系图:


f824198244a141a2b8087184f3c4763f.png


解决方案:合理规划项目,充分利用缓存等技术来减轻项目中不必要的内存占用。



频繁的垃圾回收


频繁使用大的临时变量,导致了新生代空间很快被装满,从而频繁触发垃圾回收。频繁的垃圾回收操作会让你感觉到页面卡顿。


例子:

function strToArray(str) {
    let i = 0;
    const len = str.length;
    let arr = new Uint16Array(str.length);
    for (; i < len; ++i) {
        arr[i] = str.charCodeAt(i);
    }
    return arr;
}
function foo() {
    let i = 0;
    let str = "test V8 GC";
    while (i++ < 1e5) {
        strToArray(str);
    }
}
foo();


上面这段代码就会频繁创建临时变量,这种方式很快就会造成新生代内存内装满,从而频繁触发垃圾回收。


优化策略:考虑将这些临时变量设置为全局变量。



其他场景的内存问题


来自 sugar 网友:

介绍一个场景:Node.js v4.x ,BFF 层服务端在 js 代码中写了一个 lib 模块 做 lfu、lru 的缓存,用于针对后端返回的数据进行缓存。把内存当缓存用的时候,由于线上 qps 较大的时候,缓存模块被频繁调用,造成了明显的 gc stw 现象,外部表现就是 node 对上游 http 返回逐渐变慢。由于当时上游是 nginx,且 nginx 设置了 timeout retry,因此这个内存 gc 问题当 node 返回时间超出 nginx timeout 阈值时 进而引起了 nginx 大量 retry,迅速形成雪崩效应。后来不再使用这样的当时,改为使用 node 服务器端本地文件 + redis/memcache 的缓存方案,node 做 bff 层时 确实不适合做内存当缓存这种事。

来自 Lorin 网友:



运行场景:K线行情列表


技术方案:websocket 推送二进制数据(2次/秒) -> 转换为 utf-8 格式 -> 检查数据是否相同 -> 渲染到 dom 中

出现问题:页面长时间运行后出现卡顿的现象

问题分析:将二进制数据转换为 utf-8 时,频繁触发了垃圾回收机制

解决方案:后端推送采取增量推送形式


来自 sheeeeep 网友:


介绍一下最近遇到的内存问题,非常粗暴就是 webview 页面内存占用了400多M,加上 app 本身、系统的内存占用,1G内存的移动设备直接白屏。其中部分原因是用 webaudio 加载了十多个音乐文件,用 canvas 加载了几十张小图片。图片直接改成 url 用到的时候再加载到 webgl 中,声音文件按需加载,有了很大的缓解。





目录
相关文章
|
1月前
|
Web App开发 缓存 监控
内存溢出与内存泄漏:解析与解决方案
本文深入解析内存溢出与内存泄漏的区别及成因,结合Java代码示例展示典型问题场景,剖析静态集合滥用、资源未释放等常见原因,并提供使用分析工具、优化内存配置、分批处理数据等实用解决方案,助力提升程序稳定性与性能。
595 1
|
6月前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
627 55
|
2月前
|
存储 缓存 NoSQL
工作 10 年!Redis 内存淘汰策略 LRU 和传统 LRU 差异,还傻傻分不清
小富带你深入解析Redis内存淘汰机制:LRU与LFU算法原理、实现方式及核心区别。揭秘Redis为何采用“近似LRU”,LFU如何解决频率老化问题,并结合实际场景教你如何选择合适策略,提升缓存命中率。
364 3
|
8月前
|
存储 分布式计算 监控
阿里云服务器实例经济型e、通用算力型u1、计算型c8i、通用型g8i、内存型r8i详解与选择策略
在阿里云现在的活动中,可选的云服务器实例规格主要有经济型e、通用算力型u1、计算型c8i、通用型g8i、内存型r8i实例,虽然阿里云在活动中提供了多种不同规格的云服务器实例,以满足不同用户和应用场景的需求。但是有的用户并不清楚他们的性能如何,应该如何选择。本文将详细介绍阿里云服务器中的经济型e、通用算力型u1、计算型c8i、通用型g8i、内存型r8i实例的性能、适用场景及选择参考,帮助用户根据自身需求做出更加精准的选择。
|
4月前
|
存储 人工智能 自然语言处理
AI代理内存消耗过大?9种优化策略对比分析
在AI代理系统中,多代理协作虽能提升整体准确性,但真正决定性能的关键因素之一是**内存管理**。随着对话深度和长度的增加,内存消耗呈指数级增长,主要源于历史上下文、工具调用记录、数据库查询结果等组件的持续积累。本文深入探讨了从基础到高级的九种内存优化技术,涵盖顺序存储、滑动窗口、摘要型内存、基于检索的系统、内存增强变换器、分层优化、图形化记忆网络、压缩整合策略以及类操作系统内存管理。通过统一框架下的代码实现与性能评估,分析了每种技术的适用场景与局限性,为构建高效、可扩展的AI代理系统提供了系统性的优化路径和技术参考。
246 4
AI代理内存消耗过大?9种优化策略对比分析
|
3月前
|
机器学习/深度学习 监控 安全
解密虚拟化弹性内存:五大核心技术与实施策略
本文深入解析虚拟化环境中实现内存弹性管理的五大核心技术与实施策略。内容涵盖内存架构演进、关键技术原理、性能优化方法及典型问题解决方案,助力提升虚拟机密度与资源利用率。
196 0
|
3月前
|
边缘计算 算法 Java
Java 绿色计算与性能优化:从内存管理到能耗降低的全方位优化策略与实践技巧
本文探讨了Java绿色计算与性能优化的技术方案和应用实例。文章从JVM调优(包括垃圾回收器选择、内存管理和并发优化)、代码优化(数据结构选择、对象创建和I/O操作优化)等方面提出优化策略,并结合电商平台、社交平台和智能工厂的实际案例,展示了通过Java新特性提升性能、降低能耗的显著效果。最终指出,综合运用这些优化方法不仅能提高系统性能,还能实现绿色计算目标,为企业节省成本并符合环保要求。
146 0
|
弹性计算 安全 数据库
【转】云服务器虚拟化内存优化指南:提升性能的7个关键策略
作为云计算服务核心组件,虚拟化内存管理直接影响业务系统性能表现。本文详解了内存优化方案与技术实践,助您降低30%资源浪费。
149 0
【转】云服务器虚拟化内存优化指南:提升性能的7个关键策略
|
8月前
|
机器学习/深度学习 存储 PyTorch
PyTorch内存优化的10种策略总结:在有限资源环境下高效训练模型
在大规模深度学习模型训练中,GPU内存容量常成为瓶颈,特别是在训练大型语言模型和视觉Transformer时。本文系统介绍了多种内存优化策略,包括混合精度训练、低精度训练(如BF16)、梯度检查点、梯度累积、张量分片与分布式训练、
356 14
PyTorch内存优化的10种策略总结:在有限资源环境下高效训练模型
|
8月前
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略

推荐镜像

更多