
网罗开发 (小红书、快手、视频号同名)
大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。
图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG
我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验 。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。
展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索"展菲",即可纵览我在各大平台的知识足迹。
📣 公众号"Swift社区",每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友"fzhanfei",与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!
文章目录
-
- 前言
- [为什么 WebGL 和 Vue 特别容易泄漏?](#为什么 WebGL 和 Vue 特别容易泄漏?)
-
- [原因 1:WebGL 自己占用大量 GPU/内存资源](#原因 1:WebGL 自己占用大量 GPU/内存资源)
- [原因 2:Vue 组件树引用复杂](#原因 2:Vue 组件树引用复杂)
- [原因 3:事件监听器常被遗忘](#原因 3:事件监听器常被遗忘)
- [第一步:开启 Chrome 内存分析工具](#第一步:开启 Chrome 内存分析工具)
- [用 Performance 面板录一段内存变化曲线](#用 Performance 面板录一段内存变化曲线)
- [Memory 面板:抓对象快照(Heap Snapshot)](#Memory 面板:抓对象快照(Heap Snapshot))
- [用 Allocation Timeline 查看实时对象创建](#用 Allocation Timeline 查看实时对象创建)
- [检测 WebGL 上下文是否泄漏](#检测 WebGL 上下文是否泄漏)
- [用 DevTools 查找事件监听器泄漏](#用 DevTools 查找事件监听器泄漏)
- [结合 Vue 生命周期确认销毁是否执行](#结合 Vue 生命周期确认销毁是否执行)
- [完整 Demo:基于 DevTools 的排查流程](#完整 Demo:基于 DevTools 的排查流程)
- [DevTools 诊断 Checklist(建议收藏)](#DevTools 诊断 Checklist(建议收藏))
-
- [是否只存在一个 Canvas?](#是否只存在一个 Canvas?)
- [是否有 Detached DOM 节点?](#是否有 Detached DOM 节点?)
- 是否存在未解绑事件监听器?
- [WebGL context 数量是否持续上升?](#WebGL context 数量是否持续上升?)
- [Heap Snapshot 是否存在 GeoSceneMap 残留?](#Heap Snapshot 是否存在 GeoSceneMap 残留?)
- [Allocation Timeline 是否出现疯狂创建新缓冲区?](#Allocation Timeline 是否出现疯狂创建新缓冲区?)
- [Vue 组件的 onBeforeUnmount 是否被触发?](#Vue 组件的 onBeforeUnmount 是否被触发?)
- [是否存在 Proxy 闭包引用导致 GC 失败?](#是否存在 Proxy 闭包引用导致 GC 失败?)
- 总结
前言
上一章我们聊了 Vue + GeoScene 项目中常见的内存泄漏原因,以及如何正确销毁地图实例。但很多时候,光"猜"是不够的------我要怎么证明泄漏确实存在?又要如何判断是哪一块对象没被释放?
这就是本篇的重点:
用 Chrome DevTools 进行深度内存分析,定位 WebGL + Vue 项目中的真实泄漏点。
这篇文章适合你,如果你:
- 切换页面后内存持续上涨;
- WebGL 地图总是卡、掉帧甚至崩溃;
- 在 Vue 中销毁了地图组件但内存没有下降;
- 想系统掌握前端内存分析方法。
准备好探索内存世界了吗?我们开始。
为什么 WebGL 和 Vue 特别容易泄漏?
原因 1:WebGL 自己占用大量 GPU/内存资源
WebGL 绘制地图时会创建:
- WebGLBuffer
- WebGLTexture
- WebGLProgram
- WebGLShader
- WebGLFramebuffer
这些不是 JS 对象本身,而是浏览器 GPU 层级的资源。
只靠 JS 引用释放并不一定能马上回收。
原因 2:Vue 组件树引用复杂
Vue 的响应式系统会:
- 创建 Proxy
- 缓存 Virtual DOM
- 绑定事件监听器
一旦对地图对象、图层对象、canvas 节点有闭包引用,GC(垃圾回收)就无法回收。
原因 3:事件监听器常被遗忘
GeoScene 或地图本身会绑定:
- resize
- click
- mousemove
- wheel
如果你没有卸载,浏览器会一直 hold 住这些对象。
所以调试 WebGL 内存问题,必须借助 Chrome DevTools。
第一步:开启 Chrome 内存分析工具
打开 DevTools:
Chrome → 右上角菜单 → 更多工具 → 开发者工具
切换到:
- Performance(性能)
- Memory(内存)
这两个面板是分析 WebGL 内存问题的主战场。
用 Performance 面板录一段内存变化曲线
步骤:
- 打开发生内存持续上涨的 Vue 页面;
- 打开 DevTools → Performance;
- 点击 "Record";
- 执行你的场景(例:切换地图页面 5 次);
- 停止录制。
你会看到一条类似这样的曲线:
Memory: 120MB → 150MB → 200MB → 260MB → ...
如果切换页面后内存没有下降,那么:
- 组件引用没释放
- WebGL context 没释放
- 事件监听器没解绑
- Canvas dom 节点没被移除
这是第一层诊断。
Memory 面板:抓对象快照(Heap Snapshot)
对象快照可以帮你直接找到泄漏来源。
操作步骤:
- 打开 Memory 面板
- 选择:Heap Snapshot
- 点击 Take Snapshot
- 切换地图页面 2 次
- 再拍一个快照
然后对比两个快照。
你需要找什么对象?
重点关注:
| 类型 | 是否危险 | 原因 |
|---|---|---|
HTMLCanvasElement |
高 | Canvas 节点未移除,会导致 WebGL 上下文泄漏 |
WebGLRenderingContext |
高 | 未释放 WebGL 资源 |
EventListener |
中高 | window 监听器未解绑 |
GeoSceneMap / MapView |
高 | 地图对象未销毁 |
Proxy、Object |
中 | Vue 响应式和闭包引用 |
如何判断是泄漏?
在快照中:
- 找到
Detached HTMLDivElement - 查看它的 retaining tree(引用链)
如果看到这样的内容:
Detached HTMLDivElement
↳ GeoSceneMap
↳ LayerManager
↳ window resize listener
不用怀疑 ------ 你地图实例被监听器"锁"住了,所以没有被释放。
用 Allocation Timeline 查看实时对象创建
这是确定某段操作是否"疯狂创建对象"的最强方式。
打开方式:
- Memory 面板
- 选择 Allocation instrumentation on timeline
- Record
- 执行场景
- Stop
你会看到:
- 哪些对象被持续创建;
- 哪些对象从未被释放。
如果你看到大量:
WebGLBufferWebGLTextureArrayFloat32ArrayProxy
在不停涨,那就是你的图层或渲染器在不断创造新资源但没销毁。
例子:
5000 个 WebGLBuffer,在 10 秒内创建 → 内存疯涨 → 必爆!
检测 WebGL 上下文是否泄漏
Chrome 提供一个隐藏能力:
Chrome 会显示你当前页面创建了几个 WebGL context。
打开控制台输入:
js
performance.memory
也可以查看:
js
document.querySelectorAll("canvas")
如果你切换 5 次地图页,但 canvas 从 1 个变成 5 个,说明:
地图容器没有被销毁
WebGL 上下文泄漏
内存不会下降,迟早崩
用 DevTools 查找事件监听器泄漏
Vue 和地图常见的问题是事件监听器没有移除。
打开 Chrome:
txt
Elements → Event Listeners
然后展开:
- click
- resize
- wheel
- mousemove
如果你看到:
txt
resize → 5 个监听器 →
GeoSceneMap instance
说明你页面切换 5 次,注册了 5 次 resize 事件,没有卸载。
这是许多 WebGL 框架内存泄漏的元凶!
结合 Vue 生命周期确认销毁是否执行
加一段打印代码,让你确认 Vue 销毁逻辑确实执行:
ts
onBeforeUnmount(() => {
console.log("组件销毁开始");
console.trace();
});
如果切换页面没有触发这个打印 ------
你把地图组件放在 KeepAlive 里了,当然不会销毁。
完整 Demo:基于 DevTools 的排查流程
假设你遇到的场景:
- Vue 地图页切换导致内存持续上涨
- WebGL buffer 数量持续增多
- resize 事件未解绑
下面是一段可运行的 Demo,包含问题、定位和修复。
(1)错误写法:没有解绑监听器,导致内存泄漏
ts
onMounted(() => {
map = new GeoSceneMap({ ... });
window.addEventListener('resize', () => map.resize());
});
这个匿名函数监听器无法移除,会导致 WebGL 上下文永远泄漏。
DevTools 中你会看到:
- 每切换一次页面,listener 数量 +1
- WebGLRenderingContext 数量也 +1
(2)正确写法:绑定独立函数并销毁
ts
let map: any = null;
function handleResize() {
map?.resize();
}
onMounted(() => {
map = new GeoSceneMap({...});
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
map?.destroy();
map = null;
});
再用 DevTools Memory 查看快照,引用链会消失,map 对象能正确被 GC。
DevTools 诊断 Checklist(建议收藏)
是否只存在一个 Canvas?
是否有 Detached DOM 节点?
是否存在未解绑事件监听器?
WebGL context 数量是否持续上升?
Heap Snapshot 是否存在 GeoSceneMap 残留?
Allocation Timeline 是否出现疯狂创建新缓冲区?
Vue 组件的 onBeforeUnmount 是否被触发?
是否存在 Proxy 闭包引用导致 GC 失败?
只要这份 Checklist 过了一遍,你的内存泄漏基本跑不掉。
总结
内存优化不是猜出来的,是查出来的。
借助 Chrome DevTools:
- Performance 负责看曲线;
- Memory Snapshot 负责抓引用;
- Allocation Timeline 负责查对象创建;
- Event Listener 负责查监听器泄漏;
- Heap retains tree 负责查根引用。
掌握这套方法,你不仅能搞定 GeoScene 内存泄漏,所有 WebGL、地图、可视化项目的性能瓶颈,你都能快速定位。