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;
}
防护策略:
- 始终使用
let、const或var声明变量 - 启用严格模式 (
'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 定时器未清除
问题描述: setInterval 或 setTimeout 创建的定时器未清除。
// ❌ 错误:定时器未清除
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

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



