图解 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 中,声音文件按需加载,有了很大的缓解。





目录
相关文章
|
9天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
30 6
|
7天前
|
存储 分布式计算 算法
1GB内存挑战:高效处理40亿QQ号的策略
在面对如何处理40亿个QQ号仅用1GB内存的难题时,我们需要采用一些高效的数据结构和算法来优化内存使用。这个问题涉及到数据存储、查询和处理等多个方面,本文将分享一些实用的技术策略,帮助你在有限的内存资源下处理大规模数据集。
16 1
|
9天前
|
存储 监控 Java
深入理解计算机内存管理:优化策略与实践
深入理解计算机内存管理:优化策略与实践
|
17天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
125 9
|
2月前
|
Java
在 ArkTS 中,如何有效地进行内存管理和避免内存泄漏?
【9月更文挑战第25天】在ArkTS中,有效进行内存管理并避免内存泄漏的方法包括:及时释放不再使用的资源,如关闭监听器和清理定时器;避免循环引用,通过弱引用打破循环;合理使用单例模式,确保单例对象正确释放;及时处理不再使用的页面和组件,在卸载时清理相关资源。
|
2月前
|
缓存 监控 NoSQL
阿里面试让聊一聊Redis 的内存淘汰(驱逐)策略
大家好,我是 V 哥。粉丝小 A 面试阿里时被问到 Redis 的内存淘汰策略问题,特此整理了一份详细笔记供参考。Redis 的内存淘汰策略决定了在内存达到上限时如何移除数据。希望这份笔记对你有所帮助!欢迎关注“威哥爱编程”,一起学习与成长。
|
1月前
|
存储 Kubernetes 架构师
阿里面试:JVM 锁内存 是怎么变化的? JVM 锁的膨胀过程 ?
尼恩,一位经验丰富的40岁老架构师,通过其读者交流群分享了一系列关于JVM锁的深度解析,包括偏向锁、轻量级锁、自旋锁和重量级锁的概念、内存结构变化及锁膨胀流程。这些内容不仅帮助群内的小伙伴们顺利通过了多家一线互联网企业的面试,还整理成了《尼恩Java面试宝典》等技术资料,助力更多开发者提升技术水平,实现职业逆袭。尼恩强调,掌握这些核心知识点不仅能提高面试成功率,还能在实际工作中更好地应对高并发场景下的性能优化问题。
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
2月前
|
存储 缓存 NoSQL
Redis 过期删除策略与内存淘汰策略的区别及常用命令解析
Redis 过期删除策略与内存淘汰策略的区别及常用命令解析
71 0
|
2月前
|
监控 算法 数据可视化
深入解析Android应用开发中的高效内存管理策略在移动应用开发领域,Android平台因其开放性和灵活性备受开发者青睐。然而,随之而来的是内存管理的复杂性,这对开发者提出了更高的要求。高效的内存管理不仅能够提升应用的性能,还能有效避免因内存泄漏导致的应用崩溃。本文将探讨Android应用开发中的内存管理问题,并提供一系列实用的优化策略,帮助开发者打造更稳定、更高效的应用。
在Android开发中,内存管理是一个绕不开的话题。良好的内存管理机制不仅可以提高应用的运行效率,还能有效预防内存泄漏和过度消耗,从而延长电池寿命并提升用户体验。本文从Android内存管理的基本原理出发,详细讨论了几种常见的内存管理技巧,包括内存泄漏的检测与修复、内存分配与回收的优化方法,以及如何通过合理的编程习惯减少内存开销。通过对这些内容的阐述,旨在为Android开发者提供一套系统化的内存优化指南,助力开发出更加流畅稳定的应用。
70 0

热门文章

最新文章

  • 1
    Java面试题:描述Java垃圾回收的基本原理,以及如何通过代码优化来协助垃圾回收器的工作
    88
  • 2
    Java面试题:如何在Java中触发一次Full GC?请详细解释垃圾回收机制和知识
    373
  • 3
    Java面试题:在Java中,对象何时可以被垃圾回收?编程中,如何更好地做好垃圾回收处理?
    68
  • 4
    Java面试题:解释垃圾回收中的标记-清除、复制、标记-压缩算法的工作原理
    58
  • 5
    Java面试题:解释分代垃圾回收策略,并说明其优势
    51
  • 6
    Java面试题:解释Java的垃圾回收机制,包括常见的垃圾回收算法。介绍一下Java的垃圾回收算法中的标记-压缩算法。
    49
  • 7
    Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
    68
  • 8
    Java面试题:请解释Java中的四种访问控制符及其作用范围,请解释Java中的垃圾回收机制及其工作原理,请解释Java中的并发工具包及其主要用途
    31
  • 9
    Java面试题:Java内存管理与多线程并发处理,设计一个Java应用,该应用需要处理大量并发用户请求,同时要求对内存使用进行优化,如何通过垃圾回收机制优化内存使用?
    40
  • 10
    Java面试题:Java内存管理、多线程与并发框架的面试题解析与知识点梳理,深入Java内存模型与垃圾回收机制,Java多线程机制与线程安全,Java并发工具包与框架的应用
    77
  • 相关实验场景

    更多