JavaScript内存泄漏深度分析与优化

JavaScript性能优化实战 10w+人浏览 429人参与

JavaScript内存泄漏深度分析与优化

1. 概述

JavaScript内存泄漏是Web开发中的常见问题,特别是在复杂的3D应用和WebGL场景中。本文章结合Three.js代码库做深度分析,总结JavaScript常见内存泄漏场景及其优化策略。

2. JavaScript常见内存泄漏场景

2.1 意外创建全局变量

问题描述: 未声明变量直接赋值,JavaScript会将其视为全局变量。

// ❌ 错误:意外创建全局变量
function createObject() {
   
   
    obj = new THREE.Object3D(); // 没有使用 let/const/var
    return obj;
}

// ✅ 正确:使用变量声明
function createObject() {
   
   
    const obj = new THREE.Object3D();
    return obj;
}

防护策略:

  • 始终使用 letconstvar 声明变量
  • 启用严格模式 ('use strict')
  • 使用ESLint等工具检测未声明变量

2.2 未清理的事件监听器

问题描述: 事件监听器未在适当时机移除,导致DOM元素被移除但监听器仍存在。

// ❌ 错误:未清理事件监听器
class SceneManager {
   
   
    constructor() {
   
   
        this.renderer = new THREE.WebGLRenderer();
        this.canvas = this.renderer.domElement;
        
        // 添加事件监听器但未保存引用
        this.canvas.addEventListener('click', this.handleClick);
        window.addEventListener('resize', this.handleResize);
    }
    
    // 缺少清理方法
}

// ✅ 正确:正确管理事件监听器
class SceneManager {
   
   
    constructor() {
   
   
        this.renderer = new THREE.WebGLRenderer();
        this.canvas = this.renderer.domElement;
        
        // 保存事件监听器引用
        this.boundHandleClick = this.handleClick.bind(this);
        this.boundHandleResize = this.handleResize.bind(this);
        
        this.canvas.addEventListener('click', this.boundHandleClick);
        window.addEventListener('resize', this.boundHandleResize);
    }
    
    dispose() {
   
   
        // 清理事件监听器
        this.canvas.removeEventListener('click', this.boundHandleClick);
        window.removeEventListener('resize', this.boundHandleResize);
        
        // 清理Three.js资源
        this.renderer.dispose();
    }
}

Three.js中的实际案例:

// src/renderers/WebGLRenderer.js
dispose() {
   
   
    // 清理WebGL上下文事件监听器
    canvas.removeEventListener('webglcontextlost', onContextLost, false);
    canvas.removeEventListener('webglcontextrestored', onContextRestore, false);
    canvas.removeEventListener('webglcontextcreationerror', onContextCreationError, false);
    
    // 清理XR事件监听器
    xr.removeEventListener('sessionstart', onXRSessionStart);
    xr.removeEventListener('sessionend', onXRSessionEnd);
    
    // 清理其他资源
    background.dispose();
    renderLists.dispose();
    // ... 其他清理操作
}

2.3 闭包导致的内存泄漏

问题描述: 闭包可能持有对外部变量的引用,阻止垃圾回收。

// ❌ 错误:闭包持有大对象引用
function createAnimationLoop(scene, camera, renderer) {
   
   
    const largeData = new Array(1000000).fill(0); // 大数组
    
    return function animate() {
   
   
        // 闭包持有largeData引用,即使不使用也会阻止GC
        renderer.render(scene, camera);
        requestAnimationFrame(animate);
    };
}

// ✅ 正确:避免不必要的闭包引用
function createAnimationLoop(scene, camera, renderer) {
   
   
    return function animate() {
   
   
        renderer.render(scene, camera);
        requestAnimationFrame(animate);
    };
}

// 或者使用类管理状态
class AnimationLoop {
   
   
    constructor(scene, camera, renderer) {
   
   
        this.scene = scene;
        this.camera = camera;
        this.renderer = renderer;
    }
    
    animate = () => {
   
   
        this.renderer.render(this.scene, this.camera);
        requestAnimationFrame(this.animate);
    }
}

2.4 定时器未清除

问题描述: setIntervalsetTimeout 创建的定时器未清除。

// ❌ 错误:定时器未清除
class GameLoop {
   
   
    constructor() {
   
   
        this.timer = setInterval(() => {
   
   
            this.update();
        }, 16); // 60fps
    }
    
    // 缺少清理方法
}

// ✅ 正确:正确管理定时器
class GameLoop {
   
   
    constructor() {
   
   
        this.timer = null;
    }
    
    start() {
   
   
        this.timer = setInterval(() => {
   
   
            this.update();
        }, 16);
    }
    
    stop() {
   
   
        if (this.timer) {
   
   
            clearInterval(this.timer);
            this.timer = null;
        }
    }
    
    dispose() {
   
   
        this.stop();
    }
}

Three.js中的实际案例:

// src/core/Timer.js
class Tim
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

立方世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值