You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript React+Three.js内存泄漏引发WebGLContextLost问题求助

解决Three.js + React中WebGL上下文丢失(内存泄漏)的问题

核心原因

WebGL上下文丢失(CONTEXT_LOST_WEBGL)本质是浏览器因GPU内存耗尽强制回收了WebGL上下文,你观察到的内存持续上涨(从1-10MB到30-50MB)说明存在未正确释放的Three.js资源。

针对性修复步骤

1. 修正资源清理时机

你当前的清理函数仅在组件卸载时执行,但切换帧过程中可能产生临时资源,需要在每帧切换前主动清理上一帧的冗余资源:

// 用ref存储当前帧的模型实例,方便后续清理
const currentFrameModels = useRef<THREE.Object3D[]>([]);

const switchFrame = (frameIndex: number) => {
  // 先清理上一帧的模型及关联资源
  currentFrameModels.current.forEach(model => {
    // 递归释放几何体、材质等资源
    model.traverse(obj => {
      if (obj instanceof THREE.Mesh) {
        obj.geometry.dispose();
        // 处理单材质或材质数组的情况
        if (Array.isArray(obj.material)) {
          obj.material.forEach(mat => mat.dispose());
        } else {
          obj.material.dispose();
        }
      }
    });
    scene.remove(model);
  });
  currentFrameModels.current = [];

  // 加载/更新当前帧模型位置的逻辑
  const frameData = data[frameIndex];
  // ... 你的模型位置更新或实例化代码
  // 将当前帧模型存入ref,供下一帧清理用
  currentFrameModels.current = newModels;
};

2. 修复OrbitControls的内存泄漏

OrbitControls会绑定鼠标/键盘事件,若未正确销毁监听,会导致内存泄漏。确保在组件卸载时销毁Controls:

const controlsRef = useRef<OrbitControls | null>(null);

// 初始化Controls时
controlsRef.current = new OrbitControls(camera.current!, renderer.current!.domElement);

// 组件清理函数
return () => {
  controlsRef.current?.dispose(); // 销毁Controls并移除事件监听
  renderer.current?.dispose();
  // 递归清理场景中所有剩余资源
  scene.traverse(obj => {
    if (obj instanceof THREE.Mesh) {
      obj.geometry.dispose();
      Array.isArray(obj.material) 
        ? obj.material.forEach(mat => mat.dispose()) 
        : obj.material.dispose();
    }
  });
  scene.clear();
};

3. 避免不必要的资源重复创建

如果是复用现有模型切换位置,不要每次帧切换都创建新的几何体/材质:

  • 提前加载所有需要的模型资源,存入全局ref
  • 切换帧时仅修改模型的position/visible属性,而非重新实例化模型

4. 监控资源占用定位泄漏点

在开发环境中,用Three.js内置的WebGLRenderer.info监控资源变化,快速定位泄漏类型:

// 帧切换后打印资源统计
console.log('几何体数量:', renderer.current!.info.memory.geometries);
console.log('材质数量:', renderer.current!.info.materials);
console.log('纹理数量:', renderer.current!.info.memory.textures);

如果这些数值持续上涨,说明对应类型的资源未被正确释放。

5. 不要将Three.js对象存入React状态

Mesh、Scene等Three.js核心对象必须存在useRef中,不能存入useState。状态更新会触发组件重渲染,可能导致对象被意外复制或无法回收。

额外注意事项

  • 不要频繁调用renderer.clear(),仅在必要时清理画布,过度清理会加重GPU负担
  • 若使用模型加载器(如GLTFLoader),避免重复加载相同模型,可自行实现缓存逻辑并在不需要时释放缓存资源

内容的提问来源于stack exchange,提问作者cmtrswtng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 22:22:21