{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/",
  "atlas": "https://www.pystone.net/?node=webgl-wechat-minigame-runtime-memory-analysis#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis.md",
  "context": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:webgl-wechat-minigame-runtime-memory-analysis",
  "slug": "webgl-wechat-minigame-runtime-memory-analysis",
  "title": "WebGL 应用与微信小游戏运行时架构、内存组成和分析方法",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "WebGL 应用与微信小游戏运行时架构、内存组成和分析方法 只讨论 浏览器 WebGL 应用、Unity WebGL、Emscripten/WebAssembly、微信小游戏运行时，以及它们在内存快照和 Android 真机内存分析中的表现 。其中部分示例来自本会话里的 heap snapshot 结构，例如 HEAPU8 ArrayBuffer 、 FS nameTable contents Uint8Array ArrayBu…",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "885eb0ff13abf08da64f185b6af7f759bbd485935bdee853b7290cecd5a0d59c",
  "assets": [],
  "headings": [
    {
      "depth": 1,
      "text": "WebGL 应用与微信小游戏运行时架构、内存组成和分析方法",
      "anchor": "webgl-应用与微信小游戏运行时架构内存组成和分析方法",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#webgl-%E5%BA%94%E7%94%A8%E4%B8%8E%E5%BE%AE%E4%BF%A1%E5%B0%8F%E6%B8%B8%E6%88%8F%E8%BF%90%E8%A1%8C%E6%97%B6%E6%9E%B6%E6%9E%84%E5%86%85%E5%AD%98%E7%BB%84%E6%88%90%E5%92%8C%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95"
    },
    {
      "depth": 2,
      "text": "1. WebGL 应用的整体分层架构",
      "anchor": "1-webgl-应用的整体分层架构",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#1-webgl-%E5%BA%94%E7%94%A8%E7%9A%84%E6%95%B4%E4%BD%93%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84"
    },
    {
      "depth": 3,
      "text": "1.1 从下到上的典型架构",
      "anchor": "11-从下到上的典型架构",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#11-%E4%BB%8E%E4%B8%8B%E5%88%B0%E4%B8%8A%E7%9A%84%E5%85%B8%E5%9E%8B%E6%9E%B6%E6%9E%84"
    },
    {
      "depth": 2,
      "text": "2. 浏览器 WebGL 应用的核心组成",
      "anchor": "2-浏览器-webgl-应用的核心组成",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#2-%E6%B5%8F%E8%A7%88%E5%99%A8-webgl-%E5%BA%94%E7%94%A8%E7%9A%84%E6%A0%B8%E5%BF%83%E7%BB%84%E6%88%90"
    },
    {
      "depth": 3,
      "text": "2.1 JavaScript 应用层",
      "anchor": "21-javascript-应用层",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#21-javascript-%E5%BA%94%E7%94%A8%E5%B1%82"
    },
    {
      "depth": 3,
      "text": "2.2 WebGL API 层",
      "anchor": "22-webgl-api-层",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#22-webgl-api-%E5%B1%82"
    },
    {
      "depth": 3,
      "text": "2.3 WebAssembly 层",
      "anchor": "23-webassembly-层",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#23-webassembly-%E5%B1%82"
    },
    {
      "depth": 2,
      "text": "3. Unity WebGL 的架构",
      "anchor": "3-unity-webgl-的架构",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#3-unity-webgl-%E7%9A%84%E6%9E%B6%E6%9E%84"
    },
    {
      "depth": 3,
      "text": "3.1 Unity WebGL 的编译链路",
      "anchor": "31-unity-webgl-的编译链路",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#31-unity-webgl-%E7%9A%84%E7%BC%96%E8%AF%91%E9%93%BE%E8%B7%AF"
    },
    {
      "depth": 3,
      "text": "3.2 Unity WebGL 构建产物",
      "anchor": "32-unity-webgl-构建产物",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#32-unity-webgl-%E6%9E%84%E5%BB%BA%E4%BA%A7%E7%89%A9"
    },
    {
      "depth": 3,
      "text": "3.3 Unity Heap / Wasm Heap",
      "anchor": "33-unity-heap-wasm-heap",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#33-unity-heap-wasm-heap"
    },
    {
      "depth": 2,
      "text": "4. Emscripten Runtime 的核心概念",
      "anchor": "4-emscripten-runtime-的核心概念",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#4-emscripten-runtime-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5"
    },
    {
      "depth": 3,
      "text": "4.1 Module",
      "anchor": "41-module",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#41-module"
    },
    {
      "depth": 3,
      "text": "4.2 HEAPU8 / HEAP32 / HEAPF32",
      "anchor": "42-heapu8-heap32-heapf32",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#42-heapu8-heap32-heapf32"
    },
    {
      "depth": 3,
      "text": "4.3 Wasm 线性内存",
      "anchor": "43-wasm-线性内存",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#43-wasm-%E7%BA%BF%E6%80%A7%E5%86%85%E5%AD%98"
    },
    {
      "depth": 3,
      "text": "4.4 Emscripten FS：MEMFS / IDBFS / WasmFS",
      "anchor": "44-emscripten-fsmemfs-idbfs-wasmfs",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#44-emscripten-fsmemfs-idbfs-wasmfs"
    },
    {
      "depth": 2,
      "text": "5. 微信小游戏与浏览器 WebGL 应用的区别",
      "anchor": "5-微信小游戏与浏览器-webgl-应用的区别",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#5-%E5%BE%AE%E4%BF%A1%E5%B0%8F%E6%B8%B8%E6%88%8F%E4%B8%8E%E6%B5%8F%E8%A7%88%E5%99%A8-webgl-%E5%BA%94%E7%94%A8%E7%9A%84%E5%8C%BA%E5%88%AB"
    },
    {
      "depth": 3,
      "text": "5.1 运行环境差异",
      "anchor": "51-运行环境差异",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#51-%E8%BF%90%E8%A1%8C%E7%8E%AF%E5%A2%83%E5%B7%AE%E5%BC%82"
    },
    {
      "depth": 3,
      "text": "5.2 Canvas / WebGL 差异",
      "anchor": "52-canvas-webgl-差异",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#52-canvas-webgl-%E5%B7%AE%E5%BC%82"
    },
    {
      "depth": 3,
      "text": "5.3 文件系统差异",
      "anchor": "53-文件系统差异",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#53-%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%B7%AE%E5%BC%82"
    },
    {
      "depth": 3,
      "text": "5.4 Unity WebGL 转小游戏的额外成本",
      "anchor": "54-unity-webgl-转小游戏的额外成本",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#54-unity-webgl-%E8%BD%AC%E5%B0%8F%E6%B8%B8%E6%88%8F%E7%9A%84%E9%A2%9D%E5%A4%96%E6%88%90%E6%9C%AC"
    },
    {
      "depth": 2,
      "text": "6. WebGL / 小游戏应用的内存组成",
      "anchor": "6-webgl-小游戏应用的内存组成",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#6-webgl-%E5%B0%8F%E6%B8%B8%E6%88%8F%E5%BA%94%E7%94%A8%E7%9A%84%E5%86%85%E5%AD%98%E7%BB%84%E6%88%90"
    },
    {
      "depth": 3,
      "text": "6.1 JavaScript Heap",
      "anchor": "61-javascript-heap",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#61-javascript-heap"
    },
    {
      "depth": 3,
      "text": "6.2 ArrayBuffer / TypedArray backing store",
      "anchor": "62-arraybuffer-typedarray-backing-store",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#62-arraybuffer-typedarray-backing-store"
    },
    {
      "depth": 3,
      "text": "6.3 Wasm Linear Memory / Unity Heap",
      "anchor": "63-wasm-linear-memory-unity-heap",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#63-wasm-linear-memory-unity-heap"
    },
    {
      "depth": 3,
      "text": "6.4 Wasm 编译产物和引擎内部结构",
      "anchor": "64-wasm-编译产物和引擎内部结构",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#64-wasm-%E7%BC%96%E8%AF%91%E4%BA%A7%E7%89%A9%E5%92%8C%E5%BC%95%E6%93%8E%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84"
    },
    {
      "depth": 3,
      "text": "6.5 Emscripten FS / Unity datapackage / 资源包",
      "anchor": "65-emscripten-fs-unity-datapackage-资源包",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#65-emscripten-fs-unity-datapackage-%E8%B5%84%E6%BA%90%E5%8C%85"
    },
    {
      "depth": 3,
      "text": "6.6 WebGL / GPU / Graphics 资源内存",
      "anchor": "66-webgl-gpu-graphics-资源内存",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#66-webgl-gpu-graphics-%E8%B5%84%E6%BA%90%E5%86%85%E5%AD%98"
    },
    {
      "depth": 3,
      "text": "6.7 Canvas backing store",
      "anchor": "67-canvas-backing-store",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#67-canvas-backing-store"
    },
    {
      "depth": 3,
      "text": "6.8 网络、解压和上传内存",
      "anchor": "68-网络解压和上传内存",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#68-%E7%BD%91%E7%BB%9C%E8%A7%A3%E5%8E%8B%E5%92%8C%E4%B8%8A%E4%BC%A0%E5%86%85%E5%AD%98"
    },
    {
      "depth": 3,
      "text": "6.9 图片、音频、字体和视频",
      "anchor": "69-图片音频字体和视频",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#69-%E5%9B%BE%E7%89%87%E9%9F%B3%E9%A2%91%E5%AD%97%E4%BD%93%E5%92%8C%E8%A7%86%E9%A2%91"
    },
    {
      "depth": 2,
      "text": "7. Chrome / 微信开发者工具 Heap Snapshot 术语",
      "anchor": "7-chrome-微信开发者工具-heap-snapshot-术语",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#7-chrome-%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7-heap-snapshot-%E6%9C%AF%E8%AF%AD"
    },
    {
      "depth": 3,
      "text": "7.1 Shallow Size / Self Size",
      "anchor": "71-shallow-size-self-size",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#71-shallow-size-self-size"
    },
    {
      "depth": 3,
      "text": "7.2 Retained Size",
      "anchor": "72-retained-size",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#72-retained-size"
    },
    {
      "depth": 3,
      "text": "7.3 Distance",
      "anchor": "73-distance",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#73-distance"
    },
    {
      "depth": 3,
      "text": "7.4 Retainer Path",
      "anchor": "74-retainer-path",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#74-retainer-path"
    },
    {
      "depth": 3,
      "text": "7.5 常见 node type 解读",
      "anchor": "75-常见-node-type-解读",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#75-%E5%B8%B8%E8%A7%81-node-type-%E8%A7%A3%E8%AF%BB"
    },
    {
      "depth": 2,
      "text": "8. 典型快照结构的专业解读",
      "anchor": "8-典型快照结构的专业解读",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#8-%E5%85%B8%E5%9E%8B%E5%BF%AB%E7%85%A7%E7%BB%93%E6%9E%84%E7%9A%84%E4%B8%93%E4%B8%9A%E8%A7%A3%E8%AF%BB"
    },
    {
      "depth": 3,
      "text": "8.1 system / JSArrayBufferData",
      "anchor": "81-system-jsarraybufferdata",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#81-system-jsarraybufferdata"
    },
    {
      "depth": 3,
      "text": "8.2 Module -> HEAPU8 -> ArrayBuffer",
      "anchor": "82-module---heapu8---arraybuffer",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#82-module---heapu8---arraybuffer"
    },
    {
      "depth": 3,
      "text": "8.3 FS -> nameTable -> contents -> Uint8Array",
      "anchor": "83-fs---nametable---contents---uint8array",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#83-fs---nametable---contents---uint8array"
    },
    {
      "depth": 3,
      "text": "8.4 Managed<wasm::NativeModule>",
      "anchor": "84-managedwasmnativemodule",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#84-managedwasmnativemodule"
    },
    {
      "depth": 3,
      "text": "8.5 BytecodeArray / FeedbackVector / SharedFunctionInfo",
      "anchor": "85-bytecodearray-feedbackvector-sharedfunctioninfo",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#85-bytecodearray-feedbackvector-sharedfunctioninfo"
    },
    {
      "depth": 3,
      "text": "8.6 string 大对象",
      "anchor": "86-string-大对象",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#86-string-%E5%A4%A7%E5%AF%B9%E8%B1%A1"
    },
    {
      "depth": 2,
      "text": "9. Android 真机内存指标",
      "anchor": "9-android-真机内存指标",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#9-android-%E7%9C%9F%E6%9C%BA%E5%86%85%E5%AD%98%E6%8C%87%E6%A0%87"
    },
    {
      "depth": 3,
      "text": "9.1 RSS",
      "anchor": "91-rss",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#91-rss"
    },
    {
      "depth": 3,
      "text": "9.2 PSS",
      "anchor": "92-pss",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#92-pss"
    },
    {
      "depth": 3,
      "text": "9.3 Private Dirty",
      "anchor": "93-private-dirty",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#93-private-dirty"
    },
    {
      "depth": 3,
      "text": "9.4 RssAnon / RssFile / RssShmem",
      "anchor": "94-rssanon-rssfile-rssshmem",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#94-rssanon-rssfile-rssshmem"
    },
    {
      "depth": 3,
      "text": "9.5 dumpsys meminfo 常见分类",
      "anchor": "95-dumpsys-meminfo-常见分类",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#95-dumpsys-meminfo-%E5%B8%B8%E8%A7%81%E5%88%86%E7%B1%BB"
    },
    {
      "depth": 2,
      "text": "10. 为什么“启动前 700MB，另一次启动前 800MB”很常见",
      "anchor": "10-为什么启动前-700mb另一次启动前-800mb很常见",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#10-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%90%AF%E5%8A%A8%E5%89%8D-700mb%E5%8F%A6%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E5%89%8D-800mb%E5%BE%88%E5%B8%B8%E8%A7%81"
    },
    {
      "depth": 2,
      "text": "11. 内存增长、驻留、泄露的专业定义",
      "anchor": "11-内存增长驻留泄露的专业定义",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#11-%E5%86%85%E5%AD%98%E5%A2%9E%E9%95%BF%E9%A9%BB%E7%95%99%E6%B3%84%E9%9C%B2%E7%9A%84%E4%B8%93%E4%B8%9A%E5%AE%9A%E4%B9%89"
    },
    {
      "depth": 3,
      "text": "11.1 峰值内存",
      "anchor": "111-峰值内存",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#111-%E5%B3%B0%E5%80%BC%E5%86%85%E5%AD%98"
    },
    {
      "depth": 3,
      "text": "11.2 驻留内存",
      "anchor": "112-驻留内存",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#112-%E9%A9%BB%E7%95%99%E5%86%85%E5%AD%98"
    },
    {
      "depth": 3,
      "text": "11.3 泄露",
      "anchor": "113-泄露",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#113-%E6%B3%84%E9%9C%B2"
    },
    {
      "depth": 2,
      "text": "12. WebGL 应用内存分析方法",
      "anchor": "12-webgl-应用内存分析方法",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#12-webgl-%E5%BA%94%E7%94%A8%E5%86%85%E5%AD%98%E5%88%86%E6%9E%90%E6%96%B9%E6%B3%95"
    },
    {
      "depth": 3,
      "text": "12.1 分阶段基线法",
      "anchor": "121-分阶段基线法",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#121-%E5%88%86%E9%98%B6%E6%AE%B5%E5%9F%BA%E7%BA%BF%E6%B3%95"
    },
    {
      "depth": 3,
      "text": "12.2 JS Heap Snapshot 分析步骤",
      "anchor": "122-js-heap-snapshot-分析步骤",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#122-js-heap-snapshot-%E5%88%86%E6%9E%90%E6%AD%A5%E9%AA%A4"
    },
    {
      "depth": 3,
      "text": "12.3 Android 真机分析步骤",
      "anchor": "123-android-真机分析步骤",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#123-android-%E7%9C%9F%E6%9C%BA%E5%88%86%E6%9E%90%E6%AD%A5%E9%AA%A4"
    },
    {
      "depth": 3,
      "text": "12.4 Perfetto / heapprofd",
      "anchor": "124-perfetto-heapprofd",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#124-perfetto-heapprofd"
    },
    {
      "depth": 2,
      "text": "13. 常见高风险内存路径",
      "anchor": "13-常见高风险内存路径",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#13-%E5%B8%B8%E8%A7%81%E9%AB%98%E9%A3%8E%E9%99%A9%E5%86%85%E5%AD%98%E8%B7%AF%E5%BE%84"
    },
    {
      "depth": 3,
      "text": "13.1 截图 / readPixels",
      "anchor": "131-截图-readpixels",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#131-%E6%88%AA%E5%9B%BE-readpixels"
    },
    {
      "depth": 3,
      "text": "13.2 WebGL texture / framebuffer",
      "anchor": "132-webgl-texture-framebuffer",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#132-webgl-texture-framebuffer"
    },
    {
      "depth": 3,
      "text": "13.3 FS 文件缓存",
      "anchor": "133-fs-文件缓存",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#133-fs-%E6%96%87%E4%BB%B6%E7%BC%93%E5%AD%98"
    },
    {
      "depth": 3,
      "text": "13.4 网络 response / upload body",
      "anchor": "134-网络-response-upload-body",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#134-%E7%BD%91%E7%BB%9C-response-upload-body"
    },
    {
      "depth": 3,
      "text": "13.5 Base64",
      "anchor": "135-base64",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#135-base64"
    },
    {
      "depth": 3,
      "text": "13.6 事件监听和定时器",
      "anchor": "136-事件监听和定时器",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#136-%E4%BA%8B%E4%BB%B6%E7%9B%91%E5%90%AC%E5%92%8C%E5%AE%9A%E6%97%B6%E5%99%A8"
    },
    {
      "depth": 2,
      "text": "14. 内存快照快速判断表",
      "anchor": "14-内存快照快速判断表",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#14-%E5%86%85%E5%AD%98%E5%BF%AB%E7%85%A7%E5%BF%AB%E9%80%9F%E5%88%A4%E6%96%AD%E8%A1%A8"
    },
    {
      "depth": 2,
      "text": "15. 优化原则",
      "anchor": "15-优化原则",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#15-%E4%BC%98%E5%8C%96%E5%8E%9F%E5%88%99"
    },
    {
      "depth": 3,
      "text": "15.1 延迟初始化",
      "anchor": "151-延迟初始化",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#151-%E5%BB%B6%E8%BF%9F%E5%88%9D%E5%A7%8B%E5%8C%96"
    },
    {
      "depth": 3,
      "text": "15.2 复用 buffer",
      "anchor": "152-复用-buffer",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#152-%E5%A4%8D%E7%94%A8-buffer"
    },
    {
      "depth": 3,
      "text": "15.3 避免重复副本",
      "anchor": "153-避免重复副本",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#153-%E9%81%BF%E5%85%8D%E9%87%8D%E5%A4%8D%E5%89%AF%E6%9C%AC"
    },
    {
      "depth": 3,
      "text": "15.4 显式释放 WebGL 资源",
      "anchor": "154-显式释放-webgl-资源",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#154-%E6%98%BE%E5%BC%8F%E9%87%8A%E6%94%BE-webgl-%E8%B5%84%E6%BA%90"
    },
    {
      "depth": 3,
      "text": "15.5 控制 Emscripten FS",
      "anchor": "155-控制-emscripten-fs",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#155-%E6%8E%A7%E5%88%B6-emscripten-fs"
    },
    {
      "depth": 3,
      "text": "15.6 控制 Wasm heap 峰值",
      "anchor": "156-控制-wasm-heap-峰值",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#156-%E6%8E%A7%E5%88%B6-wasm-heap-%E5%B3%B0%E5%80%BC"
    },
    {
      "depth": 3,
      "text": "15.7 用阶段差分而不是单点比较",
      "anchor": "157-用阶段差分而不是单点比较",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#157-%E7%94%A8%E9%98%B6%E6%AE%B5%E5%B7%AE%E5%88%86%E8%80%8C%E4%B8%8D%E6%98%AF%E5%8D%95%E7%82%B9%E6%AF%94%E8%BE%83"
    },
    {
      "depth": 2,
      "text": "16. 推荐的分析流程模板",
      "anchor": "16-推荐的分析流程模板",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#16-%E6%8E%A8%E8%8D%90%E7%9A%84%E5%88%86%E6%9E%90%E6%B5%81%E7%A8%8B%E6%A8%A1%E6%9D%BF"
    },
    {
      "depth": 3,
      "text": "16.1 证明是否增长",
      "anchor": "161-证明是否增长",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#161-%E8%AF%81%E6%98%8E%E6%98%AF%E5%90%A6%E5%A2%9E%E9%95%BF"
    },
    {
      "depth": 3,
      "text": "16.2 证明是否泄露",
      "anchor": "162-证明是否泄露",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#162-%E8%AF%81%E6%98%8E%E6%98%AF%E5%90%A6%E6%B3%84%E9%9C%B2"
    },
    {
      "depth": 3,
      "text": "16.3 归因路径",
      "anchor": "163-归因路径",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#163-%E5%BD%92%E5%9B%A0%E8%B7%AF%E5%BE%84"
    },
    {
      "depth": 2,
      "text": "17. 标准术语表",
      "anchor": "17-标准术语表",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#17-%E6%A0%87%E5%87%86%E6%9C%AF%E8%AF%AD%E8%A1%A8"
    },
    {
      "depth": 2,
      "text": "18. 最重要的心智模型",
      "anchor": "18-最重要的心智模型",
      "citation": "https://www.pystone.net/notes/webgl-wechat-minigame-runtime-memory-analysis/#18-%E6%9C%80%E9%87%8D%E8%A6%81%E7%9A%84%E5%BF%83%E6%99%BA%E6%A8%A1%E5%9E%8B"
    }
  ],
  "claims": [],
  "outgoing": [],
  "incoming": [
    {
      "id": "note:game-graphics-and-runtime",
      "title": "游戏图形与运行时",
      "url": "https://www.pystone.net/notes/game-graphics-and-runtime/",
      "atlas": "https://www.pystone.net/?node=game-graphics-and-runtime#knowledge-atlas",
      "label": "游戏图形与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "WebGL中的“WebGL 应用与微信小游戏运行时架构、内存组成和分析方法”导航项",
      "citation": "https://www.pystone.net/notes/game-graphics-and-runtime/#webgl"
    },
    {
      "id": "note:game-dev-graphics-performance",
      "title": "游戏研发、图形与性能",
      "url": "https://www.pystone.net/notes/game-dev-graphics-performance/",
      "atlas": "https://www.pystone.net/?node=game-dev-graphics-performance#knowledge-atlas",
      "label": "游戏研发、图形与性能",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "WebGL 与微信小游戏运行时架构连接浏览器平台、内存组成和分析方法。",
      "citation": "https://www.pystone.net/notes/game-dev-graphics-performance/#%E4%BB%8E%E6%9E%B6%E6%9E%84%E5%88%B0%E8%BF%90%E8%A1%8C%E6%97%B6"
    }
  ],
  "contentMarkdown": "# WebGL 应用与微信小游戏运行时架构、内存组成和分析方法\n只讨论 **浏览器 WebGL 应用、Unity WebGL、Emscripten/WebAssembly、微信小游戏运行时，以及它们在内存快照和 Android 真机内存分析中的表现**。其中部分示例来自本会话里的 heap snapshot 结构，例如 `HEAPU8 -> ArrayBuffer`、`FS -> nameTable -> contents -> Uint8Array -> ArrayBuffer` 这类 retainer path，用作通用解读样例。\n\n---\n\n## 1. WebGL 应用的整体分层架构\n\n一个现代 WebGL 游戏或图形应用，通常不是“JavaScript 调用 GPU”这么简单，而是由多层运行时组成。理解内存时，必须先分清每一层。\n\n### 1.1 从下到上的典型架构\n\n```text\n硬件层\n  CPU / RAM / GPU / 显存 / 显示系统\n    ↓\n操作系统层\n  Android / iOS / Windows / macOS\n  进程管理、内存页、图形驱动、文件系统、网络\n    ↓\n浏览器或宿主运行时\n  Chrome / Safari / Firefox / Edge\n  或微信小游戏宿主、XWeb、小游戏 JS Runtime\n    ↓\nJavaScript 引擎\n  V8 / JavaScriptCore / QuickJS-like runtime / 宿主自研封装\n  负责 JS 对象、闭包、字符串、ArrayBuffer、GC、JIT/Bytecode\n    ↓\nWebAssembly 引擎\n  加载 .wasm\n  编译 Wasm code\n  管理 WebAssembly.Memory、Table、Instance、Module\n    ↓\nWebGL 实现层\n  WebGLRenderingContext / WebGL2RenderingContext\n  浏览器或宿主把 WebGL API 转成 OpenGL ES / Metal / Vulkan / Direct3D / ANGLE / 原生图形 API\n    ↓\nCanvas 层\n  HTMLCanvasElement 或小游戏 Canvas\n  作为 WebGL 绘制目标\n    ↓\nEmscripten Runtime\n  Module、HEAPU8、FS、dynCall、wasm exports、JS glue code\n    ↓\n游戏引擎运行时\n  Unity WebGL / Cocos / Laya / PlayCanvas / Three.js / Babylon.js\n    ↓\n游戏业务层\n  C# / TypeScript / JavaScript / Lua / C++ 逻辑\n  场景、资源、UI、网络、插件、监控、截图、音频\n```\n\n每一层都可能分配内存，而且这些内存不一定都能在 JavaScript heap snapshot 里看到。\n\n---\n\n## 2. 浏览器 WebGL 应用的核心组成\n\n### 2.1 JavaScript 应用层\n\n普通 WebGL 应用的上层通常是 JavaScript 或 TypeScript。它负责：\n\n```text\n1. 创建 Canvas；\n2. 获取 WebGL context；\n3. 加载资源；\n4. 管理渲染循环；\n5. 创建 shader / texture / buffer / framebuffer；\n6. 处理输入、网络、音频、UI；\n7. 与 Wasm 或引擎 runtime 通信。\n```\n\n常见代码形态：\n\n```js\nconst canvas = document.createElement(\"canvas\");\nconst gl = canvas.getContext(\"webgl2\") || canvas.getContext(\"webgl\");\n\nconst tex = gl.createTexture();\nconst fbo = gl.createFramebuffer();\nconst buffer = gl.createBuffer();\n```\n\n这类 JS 对象本身通常很小，但它们对应的底层 native / GPU 资源可能很大。\n\n---\n\n### 2.2 WebGL API 层\n\nWebGL 是暴露给 JavaScript 的图形 API。它的对象通常包括：\n\n```text\nWebGLTexture        纹理对象\nWebGLBuffer         顶点 / 索引 / uniform buffer\nWebGLFramebuffer    离屏渲染目标\nWebGLRenderbuffer   深度 / 模板 / 多采样 renderbuffer\nWebGLShader         顶点 / 片元 shader\nWebGLProgram        shader program\nWebGLQuery          GPU query\nWebGLSync           GPU 同步对象\n```\n\n例如 `framebufferTexture2D()` 会把一个 texture 附加到 framebuffer 上，作为 framebuffer 的颜色、深度或模板附件。([MDN Web Docs][1])\n\n内存理解重点：\n\n```text\nJS heap 里看到的 WebGLTexture 对象：\n  只是一个 JS wrapper。\n\n真正的大内存：\n  可能在 GPU 显存、图形驱动、浏览器 renderer native memory、Android Graphics / GL mtrack 中。\n```\n\n所以，WebGL 纹理泄露、FBO 泄露、PBO 泄露，经常 **不会** 在 JS heap snapshot 中表现为大对象，但会在 Android `dumpsys meminfo` 的 `Graphics`、`GL mtrack`、`EGL mtrack`、`Private Other`、`RssAnon` 等指标里表现出来。\n\n---\n\n### 2.3 WebAssembly 层\n\nWebAssembly 是浏览器中运行 C/C++/Rust/C# 转译代码的低层执行格式。Unity WebGL、部分 Cocos 模块、物理引擎、音视频编码器、图像编码器都可能使用 Wasm。\n\nWebAssembly 应用通常包含：\n\n```text\n.wasm 文件\n  编译后的二进制代码。\n\nWebAssembly.Module\n  解析后的 Wasm 模块。\n\nWebAssembly.Instance\n  实例化后的运行实例。\n\nWebAssembly.Memory\n  Wasm 线性内存。\n\nWebAssembly.Table\n  函数表，常用于间接调用、函数指针、dynCall。\n```\n\n在 heap snapshot 中，常见表现包括：\n\n```text\nModule\nTable\nWasmDispatchTable\nWasmTrustedInstanceData\nManaged<wasm::NativeModule>\nWebAssembly.Memory.buffer\nArrayBuffer\nsystem / JSArrayBufferData\n```\n\n`Managed<wasm::NativeModule>` 通常表示 V8 / JS 引擎保存的 Wasm 编译产物、元数据或 native module 结构。它不是业务 JS 对象，也不一定能由业务代码直接释放。\n\n---\n\n## 3. Unity WebGL 的架构\n\nUnity WebGL 是理解小游戏 WebGL 内存的核心场景之一。\n\n### 3.1 Unity WebGL 的编译链路\n\nUnity WebGL 的大致编译链路是：\n\n```text\nC# 游戏代码\n  ↓ IL2CPP\nC++ 代码\n  ↓ Emscripten\nWebAssembly\n\nUnity 引擎 C/C++ runtime\n  ↓ Emscripten\nWebAssembly\n\nUnity WebGL loader / framework JS\n  ↓\n浏览器或小游戏宿主\n```\n\nUnity 文档说明，Unity WebGL 使用 Emscripten 将 Unity runtime 的 C/C++ 代码交叉编译成 WebAssembly；C# 脚本则通过 IL2CPP 转成 C++，再由 Emscripten 编译成 Wasm。([Unity Documentation][2])\n\n---\n\n### 3.2 Unity WebGL 构建产物\n\nUnity WebGL 构建后通常会产生：\n\n```text\nindex.html\n  浏览器入口页面。\n\n.loader.js\n  加载器脚本。\n\n.framework.js\n  Unity WebGL JavaScript framework / glue code。\n\n.wasm\n  Unity runtime + IL2CPP 代码编译后的 Wasm。\n\n.data / .data.unityweb / data.unity3d\n  资源数据包，包含场景、资源、序列化数据等。\n\nStreamingAssets / AssetBundles / Addressables\n  运行时资源。\n```\n\nUnity 官方文档也说明 Web Build folder 会包含运行 Web 应用所需的文件。([Unity Documentation][3])\n\n---\n\n### 3.3 Unity Heap / Wasm Heap\n\nUnity WebGL 有一个非常重要的内存概念：**Unity Heap**。\n\nUnity 文档说明，Unity heap 是一个连续内存块，用于保存 Unity runtime objects，包括 managed/native objects、assets、scenes、shaders 等；这个 heap 会以 WebAssembly Memory 的形式存在，其 `buffer` 是一个可调整大小的 `ArrayBuffer`。([Unity Documentation][4])\n\n在 heap snapshot 中，它通常表现为：\n\n```text\nModule\n  -> HEAPU8\n    -> Uint8Array\n      -> ArrayBuffer\n        -> system / JSArrayBufferData\n```\n\n示例 retainer path：\n\n```text\nArrayBuffer retained=256MB\n  <- Uint8Array\n  <- HEAPU8\n  <- Module\n```\n\n本会话的快照示例中也出现了 `ArrayBuffer 256MB -> HEAPU8` 这种典型结构。\n\n专业解释：\n\n```text\nHEAPU8 不是一块新内存。\nHEAPU8 是 WebAssembly.Memory.buffer 的 Uint8Array 视图。\n真正的大内存是 backing store，也就是 system / JSArrayBufferData。\n```\n\n---\n\n## 4. Emscripten Runtime 的核心概念\n\nEmscripten 是把 C/C++ 编译到 WebAssembly 的工具链。Unity WebGL、很多 C++ 游戏引擎、图像编码器、物理库都会间接使用它。\n\n### 4.1 Module\n\n`Module` 是 Emscripten JavaScript glue code 的核心对象。它通常保存：\n\n```text\nModule.HEAP8\nModule.HEAPU8\nModule.HEAP16\nModule.HEAPU16\nModule.HEAP32\nModule.HEAPU32\nModule.HEAPF32\nModule.HEAPF64\n\nModule._malloc\nModule._free\nModule.ccall\nModule.cwrap\nModule.FS\nModule.canvas\nModule.asm\nModule.wasmMemory\n```\n\n`Module` 本身不一定很大，但它能引用很多大对象。例如：\n\n```text\nModule.HEAPU8.buffer\nModule.FS.nameTable\nModule.canvas\nModule.asm.__indirect_function_table\n```\n\n所以 heap snapshot 中看到 `Module` 作为 retainer 时，不能直接说“Module 分配了这些内存”，更准确的话术是：\n\n> `Module` 是 Emscripten runtime 的根对象，它让 Wasm heap、FS、函数表等结构保持可达。\n\n---\n\n### 4.2 HEAPU8 / HEAP32 / HEAPF32\n\n这些是对同一个 `WebAssembly.Memory.buffer` 的不同 typed array 视图。\n\n```text\nHEAPU8   Uint8Array，按字节访问 Wasm 内存\nHEAP8    Int8Array\nHEAPU16  Uint16Array\nHEAP32   Int32Array\nHEAPF32  Float32Array\nHEAPF64  Float64Array\n```\n\n它们都不是独立的大内存。真正大的是：\n\n```text\nWebAssembly.Memory.buffer\n  -> ArrayBuffer\n    -> native: system / JSArrayBufferData\n```\n\n### 4.3 Wasm 线性内存\n\nWasm 线性内存可以理解为 C/C++ 程序看到的一整块连续地址空间。C++ 里的：\n\n```cpp\nmalloc()\nnew\nstd::vector\nstd::string\nUnity native objects\nIL2CPP managed heap\ndecoded asset data\ntemporary buffers\n```\n\n最终很多都会落在这块线性内存里。\n\n关键特征：\n\n```text\n1. Wasm heap 增长后通常不会自动缩回。\n2. C++ 对象释放后，只是 allocator 内部可复用，不代表 WebAssembly.Memory.buffer 变小。\n3. JavaScript heap snapshot 能看到 Wasm Memory 的 ArrayBuffer 容量，但不一定能知道里面哪些 C++ 对象占用。\n4. 如果 heap snapshot 前后 HEAPU8.buffer.byteLength 没变，不代表 C++ 内部没有分配变化；需要看 sbrk、malloc 统计或引擎内部内存统计。\n```\n\n---\n\n### 4.4 Emscripten FS：MEMFS / IDBFS / WasmFS\n\nEmscripten 提供一个虚拟文件系统，使 C/C++ 代码可以使用类似 POSIX 的文件 API。\n\n常见文件系统：\n\n```text\nMEMFS\n  内存文件系统。\n  文件内容保存在 JS/Wasm 内存中。\n  默认经常挂载在 /。\n\nIDBFS\n  IndexedDB-backed 文件系统。\n  通常先在内存中维护文件，再与 IndexedDB 同步。\n\nNODEFS\n  Node.js 环境下映射本地文件系统。\n\nWasmFS\n  新的 Wasm-based 文件系统层。\n```\n\nEmscripten 文档说明，MEMFS 会在 runtime 初始化时挂载到 `/`；预加载文件可以在启动时放进 MEMFS。([Emscripten][5]) Emscripten 也有专门的 File System API 文档，并说明 WasmFS 是新的高性能、多线程文件系统层。([Emscripten][6])\n\n在 heap snapshot 中，MEMFS 文件常见 retainer path 是：\n\n```text\nFS\n  -> nameTable\n    -> FSNode\n      -> contents\n        -> Uint8Array\n          -> ArrayBuffer\n            -> system / JSArrayBufferData\n```\n\n本会话的快照示例中就出现了 `FS -> nameTable -> contents -> Uint8Array -> ArrayBuffer 43MB` 的结构。\n\n专业解释：\n\n> 这条链说明“大内存是一个 Emscripten FS 文件的内容”。\n> 它不能单独证明这个文件是谁写入的，需要结合文件路径、内容头、日志或 FS dump 才能归因。\n\n---\n\n## 5. 微信小游戏与浏览器 WebGL 应用的区别\n\n微信小游戏可以运行 WebGL 和 Wasm，但它不是标准浏览器页面。\n\n### 5.1 运行环境差异\n\n标准浏览器 WebGL 应用通常运行在：\n\n```text\nHTML 页面\nDOM\nCanvas\n浏览器网络 API\n浏览器存储 API\n标准 DevTools\n```\n\n微信小游戏运行在：\n\n```text\n微信宿主 App\n小游戏 JS runtime\n微信封装的 Canvas / WebGL API\nwx API\n小游戏文件系统\n小游戏网络 API\n小游戏包体 / 分包 / 资源缓存机制\n微信开发者工具 / 真机远程调试\n```\n\nCocos Creator 文档对微信小游戏的描述很适合作为通用理解：微信小游戏运行环境是微信小程序环境的扩展，在小程序环境基础上提供了 WebGL 接口封装，但这些接口由微信团队通过原生实现封装，不能等同于浏览器环境。([Cocos Creator][7])\n\n因此，同样一段 WebGL 或 Unity WebGL 代码：\n\n```text\n在 Chrome 浏览器里：\n  内存可能表现为 renderer process + GPU process。\n\n在微信小游戏里：\n  内存可能表现为 com.tencent.mm:appbrandX、tools、xweb、GPU/native mtrack、Private Other 等多个进程或类别。\n```\n\n---\n\n### 5.2 Canvas / WebGL 差异\n\n浏览器中：\n\n```js\nconst canvas = document.querySelector(\"canvas\");\nconst gl = canvas.getContext(\"webgl\");\n```\n\n小游戏中通常是：\n\n```js\nconst canvas = wx.createCanvas();\nconst gl = canvas.getContext(\"webgl\");\n```\n\n差异包括：\n\n```text\n1. 没有完整 DOM。\n2. Canvas 由小游戏宿主创建和管理。\n3. WebGL API 由宿主封装，底层可能走微信/XWeb/平台图形实现。\n4. 部分扩展、像素读取、canvas 导出、文件转换行为可能和浏览器不同。\n5. 真机内存归因通常更依赖 Android/iOS 系统工具，而不是单靠 JS heap snapshot。\n```\n\n---\n\n### 5.3 文件系统差异\n\n标准浏览器 WebGL：\n\n```text\nEmscripten MEMFS / IDBFS\nBrowser Cache\nIndexedDB\nFetch / XHR response cache\n```\n\n微信小游戏：\n\n```text\n小游戏包体\n分包\n远程资源\n微信本地缓存目录\n小游戏文件系统 API\nUnity / Emscripten 虚拟 FS\n宿主内部资源缓存\n```\n\n在小游戏里，Emscripten FS、Unity datapackage、小游戏宿主缓存、网络下载缓存可能同时存在。它们在内存中可能表现为：\n\n```text\nJS ArrayBuffer\nUint8Array\nFSNode.contents\nNative file buffer\nNetwork response buffer\nDecompression buffer\n小游戏宿主内部 cache\n```\n\n---\n\n### 5.4 Unity WebGL 转小游戏的额外成本\n\nUnity / 团结引擎的小游戏 WebGL 平台文档指出，WebGL 运行机制和文件系统让内存结构与原生 App 很不一样，通常会多出 Wasm 加载与编译、Wasm Heap 未使用部分、JS File System 等内存；文档还特别提示，Emscripten 使用 JS + IndexedDB 模拟文件系统时，JS 中可能始终存有文件 copy，因此在微信小游戏中应尽量避免使用 Emscripten 文件系统。([Unity User Manual][8])\n\n这对内存分析很重要：\n\n```text\n小游戏 WebGL 内存高，不一定是业务泄露。\n它可能是：\n  Wasm 文件加载和编译成本；\n  Unity Heap 预留容量；\n  data package / FS 文件内容；\n  浏览器/宿主 JIT 和 code cache；\n  GPU/Canvas/WebGL native 资源；\n  资源下载、解压、缓存；\n  多进程 PSS 统计口径导致的波动。\n```\n\n---\n\n## 6. WebGL / 小游戏应用的内存组成\n\n从内存分析角度，可以把 WebGL 应用内存分成以下几类。\n\n---\n\n### 6.1 JavaScript Heap\n\n包括：\n\n```text\n普通 Object\nArray\nMap / Set / WeakMap\nClosure\nFunction\nPromise\n字符串\nTypedArray wrapper\nArrayBuffer wrapper\n事件回调\n定时器回调\n模块对象\n小游戏 runtime JS 对象\n引擎 JS glue code\n```\n\n在 heap snapshot 中常见类型：\n\n```text\nobject\narray\nstring\nconcatenated string\nclosure\ncode\nobject shape / system Map\nsystem / Context\n```\n\n注意：\n\n```text\nArrayBuffer wrapper 很小。\n真正的二进制 backing store 通常体现在 native: system / JSArrayBufferData。\n```\n\n---\n\n### 6.2 ArrayBuffer / TypedArray backing store\n\n典型对象链：\n\n```text\nUint8Array\n  -> buffer\n    -> ArrayBuffer\n      -> backing_store\n        -> system / JSArrayBufferData\n```\n\n常见来源：\n\n```text\n1. Wasm Memory buffer；\n2. 网络 response ArrayBuffer；\n3. 文件读取结果；\n4. 图片/音频/压缩包二进制；\n5. WebGL readPixels 结果；\n6. Emscripten FS 文件 contents；\n7. 上传 request body；\n8. 解压缩中间 buffer；\n9. 编码器输入 / 输出 buffer。\n```\n\n在 heap snapshot 中，如果看到：\n\n```text\nnative | system / JSArrayBufferData | 43MB\n```\n\n不要停在这个节点，要往 retainer path 往上看：\n\n```text\n是 HEAPU8 持有？\n是 FSNode.contents 持有？\n是 Response / Promise 持有？\n是 Upload queue 持有？\n是 WebGL readback buffer 持有？\n```\n\n本会话快照示例中 `system / JSArrayBufferData` 排名前两项是 256MB 和 43MB，说明大头是 ArrayBuffer backing store，而不是普通 JS 对象。\n\n---\n\n### 6.3 Wasm Linear Memory / Unity Heap\n\n表现：\n\n```text\nModule.HEAPU8.buffer\nArrayBuffer\nsystem / JSArrayBufferData\n```\n\n常见大小：\n\n```text\n64MB\n128MB\n192MB\n256MB\n512MB\n更高\n```\n\n取决于：\n\n```text\nINITIAL_MEMORY\nALLOW_MEMORY_GROWTH\nMAXIMUM_MEMORY\nUnity Player Settings\n运行时峰值分配\n资源规模\nC++ allocator 行为\n```\n\n分析重点：\n\n```text\nHEAPU8.buffer.byteLength：\n  表示 Wasm heap 容量。\n\nC++ 已使用内存：\n  需要看 malloc/sbrk/Unity profiler/引擎统计。\n\n释放 C++ 对象：\n  不一定让 HEAPU8.buffer 变小。\n\nWasm heap 增长：\n  一旦扩容，通常成为新的常驻高水位。\n```\n\n---\n\n### 6.4 Wasm 编译产物和引擎内部结构\n\nheap snapshot 中可能看到：\n\n```text\nManaged<wasm::NativeModule>\nWasmTrustedInstanceData\nWasmDispatchTable\nWasmImportData\nTable\nCode\nBytecodeArray\nFeedbackCell\nFeedbackVector\nSharedFunctionInfo\n```\n\n专业解释：\n\n```text\nManaged<wasm::NativeModule>\n  Wasm 编译后的 native module / 引擎内部结构。\n\nWasmDispatchTable / Table\n  Wasm 函数表、间接调用表、函数指针相关结构。\n\nBytecodeArray / FeedbackVector / Code\n  JavaScript 引擎为 JS 函数生成的字节码、反馈向量、优化信息。\n\nWAGame.js / framework.js / plugin.js string\n  运行时代码或源码字符串，不一定是泄露。\n```\n\n本会话快照示例中存在 `Managed<wasm::NativeModule> 35.23MB`、`WasmTrustedInstanceData`、`WasmDispatchTable`、`Table` 等，这类对象通常属于 Wasm/JS 引擎运行时成本。\n\n---\n\n### 6.5 Emscripten FS / Unity datapackage / 资源包\n\n典型 retainer path：\n\n```text\nFS\n  -> nameTable\n    -> Array[index]\n      -> FSNode\n        -> contents\n          -> Uint8Array\n            -> ArrayBuffer\n              -> JSArrayBufferData\n```\n\n可能来源：\n\n```text\n1. Unity data package；\n2. 首包资源；\n3. 预加载文件；\n4. 运行时写入的临时文件；\n5. 下载缓存；\n6. 截图临时文件；\n7. 日志文件；\n8. AssetBundle / Addressables cache。\n```\n\n重要判断：\n\n```text\nFS -> nameTable -> contents 只能证明“这是 FS 文件内容”。\n不能直接证明“这是哪个模块写入的”。\n必须进一步看：\n  文件路径；\n  文件头；\n  FS dump；\n  写入调用栈；\n  运行日志；\n  阶段差分。\n```\n\n如果文件头是 `UnityWebData1.0`，通常更像 Unity datapackage，而不是业务临时缓存。\n\n---\n\n### 6.6 WebGL / GPU / Graphics 资源内存\n\n常见资源：\n\n```text\nTexture\nRenderbuffer\nFramebuffer\nVertex Buffer\nIndex Buffer\nUniform Buffer\nShader\nProgram\nPBO / Pixel Buffer Object\nVAO\nTransform feedback\n```\n\n这些资源大多不在 JS heap 中以真实大小体现。\n\n典型内存估算：\n\n```text\nRGBA8 texture:\n  width × height × 4 bytes\n\nRGBA8 texture with mipmaps:\n  width × height × 4 × 1.33 左右\n\nDepth24/Stencil8:\n  width × height × 4 bytes\n\n双缓冲 / 多缓冲:\n  单份大小 × buffer count\n\nPBO ring:\n  requiredBytes × ringSize\n```\n\n例如：\n\n```text\n1280 × 720 × 4 ≈ 3.52 MB\n1920 × 1080 × 4 ≈ 7.91 MB\n2560 × 1440 × 4 ≈ 14.06 MB\n三份 2560×1440 RGBA ≈ 42.19 MB\n```\n\n所以“截图、离屏渲染、readPixels、PBO ring、canvas 导出”很容易产生 20–50MB 级别的波动，但不一定在 JS heap snapshot 中出现。\n\n---\n\n### 6.7 Canvas backing store\n\nCanvas 本身也有 backing store。实际像素尺寸通常是：\n\n```text\ncssWidth × devicePixelRatio\ncssHeight × devicePixelRatio\n```\n\n例如：\n\n```text\nCSS 1280×720\ndevicePixelRatio = 2\n实际 backing store = 2560×1440\nRGBA = 2560×1440×4 ≈ 14.06 MB\n```\n\n如果同时有：\n\n```text\n主 Canvas\n离屏 Canvas\n截图 Canvas\n临时 Canvas\nWebGL framebuffer\n导出图片 buffer\n```\n\n内存会快速增加。\n\n---\n\n### 6.8 网络、解压和上传内存\n\n常见链路：\n\n```text\nfetch / wx.request\n  -> response ArrayBuffer\n  -> 解压 buffer\n  -> 解析 buffer\n  -> 写入 FS 或 Wasm heap\n  -> 上传 body copy\n```\n\n常见重复副本：\n\n```text\n网络 response buffer\n解压前 compressed buffer\n解压后 uncompressed buffer\nWasm heap copy\nFS contents copy\n业务 cache copy\n上传 request body copy\n宿主内部 native copy\n```\n\n优化目标不是“完全没有 buffer”，而是减少同一份数据在同一时间窗口内出现多份副本。\n\n---\n\n### 6.9 图片、音频、字体和视频\n\n图片资源可能经历：\n\n```text\ncompressed image file\ndecoded RGBA pixels\nGPU texture\nmipmap chain\nCPU-side cache\n```\n\n音频资源可能经历：\n\n```text\ncompressed audio file\ndecoded PCM\naudio engine buffer\nstreaming buffer\nnative audio cache\n```\n\n字体资源可能经历：\n\n```text\nfont file\nglyph cache\ntexture atlas\nlayout cache\n```\n\n这些通常也不完全体现在 JS heap snapshot 中。\n\n---\n\n## 7. Chrome / 微信开发者工具 Heap Snapshot 术语\n\nChrome DevTools 文档把 heap snapshot 用于分析对象分布和内存泄露；Memory 面板里常见 `Shallow Size` 和 `Retained Size` 两个概念。([Chrome for Developers][9])\n\n### 7.1 Shallow Size / Self Size\n\n含义：\n\n```text\n对象自身占用的内存。\n```\n\n例如：\n\n```text\nArrayBuffer wrapper self_size = 52 B\nUint8Array wrapper self_size = 60 B\n```\n\n这不代表 buffer 只有几十字节。真正数据可能在 backing store。\n\n---\n\n### 7.2 Retained Size\n\n含义：\n\n```text\n如果这个对象被释放，并且它支配的对象也变为不可达，总共可以释放的内存。\n```\n\nRetained Size 适合找 dominator，但要小心：\n\n```text\n某个 Object retained 很大，可能只是因为它是一个根容器。\n它不一定是分配源。\n```\n\n例如：\n\n```text\nFS object retained 43MB\n```\n\n专业解释不是“FS 对象自己有 43MB”，而是：\n\n> FS 通过 nameTable 支配了某个 FSNode.contents，而 contents 持有 43MB ArrayBuffer。\n\n---\n\n### 7.3 Distance\n\n含义：\n\n```text\n对象到 GC Root 的最短引用距离。\n```\n\n距离越小，越接近根对象。常见根：\n\n```text\nwindow / globalThis\nGameGlobal\nModule\nGC roots\nGlobal handles\nNative contexts\nInternalized strings\n```\n\n距离小不代表泄露，只说明它容易长期存活。\n\n---\n\n### 7.4 Retainer Path\n\nRetainer path 是：\n\n```text\n谁持有了这个对象？\n这个对象为什么还活着？\n```\n\n它不是 allocation stack。\n\n正确表述：\n\n```text\nRetainer path 证明“当前由谁保活”。\nAllocation stack 才证明“当初由谁分配”。\n```\n\n例如：\n\n```text\nFS -> nameTable -> contents -> Uint8Array -> ArrayBuffer\n```\n\n只能证明：\n\n```text\n这个 ArrayBuffer 当前作为一个 FS 文件内容存在。\n```\n\n不能单凭这条链判断：\n\n```text\n是谁写了这个文件？\n这个文件是什么？\n是否是泄露？\n```\n\n---\n\n### 7.5 常见 node type 解读\n\n| type        | 含义                | 常见来源                                    |\n| ----------- | ----------------- | --------------------------------------- |\n| `object`    | JS 对象             | Object、ArrayBuffer、Uint8Array、Module、FS |\n| `array`     | JS 数组或内部数组        | object elements、nameTable、属性数组          |\n| `string`    | 字符串               | 源码字符串、日志、JSON、base64                    |\n| `code`      | JS 引擎代码对象         | BytecodeArray、Code、SharedFunctionInfo   |\n| `closure`   | 函数闭包              | 回调、模块函数、事件监听                            |\n| `native`    | JS 引擎外部/native 对象 | JSArrayBufferData                       |\n| `hidden`    | 引擎内部对象            | WasmNativeModule、Map、Context            |\n| `synthetic` | 快照合成根节点           | GC roots、Global handles                 |\n\n---\n\n## 8. 典型快照结构的专业解读\n\n### 8.1 `system / JSArrayBufferData`\n\n看到：\n\n```text\nnative: system / JSArrayBufferData 256MB\n```\n\n专业解读：\n\n```text\n这是某个 ArrayBuffer 的外部 backing store。\n它是字节数据真正占用的地方。\n需要往上找 backing_store 的 owner ArrayBuffer。\n```\n\n继续看 retainer：\n\n```text\nJSArrayBufferData\n  <- backing_store\nArrayBuffer\n  <- buffer\nUint8Array\n  <- HEAPU8\nModule\n```\n\n结论：\n\n```text\n这是 Wasm linear memory / Unity Heap。\n```\n\n如果 retainer 是：\n\n```text\nJSArrayBufferData\n  <- ArrayBuffer\n  <- Uint8Array\n  <- contents\nFSNode\n  <- nameTable\nFS\n```\n\n结论：\n\n```text\n这是 Emscripten FS 文件内容。\n```\n\n---\n\n### 8.2 `Module -> HEAPU8 -> ArrayBuffer`\n\n专业含义：\n\n```text\nModule 是 Emscripten runtime 全局对象。\nHEAPU8 是 Wasm memory 的 Uint8Array 视图。\nArrayBuffer 是 WebAssembly.Memory.buffer。\nJSArrayBufferData 是 backing store。\n```\n\n不要说：\n\n```text\nModule 分配了 256MB。\n```\n\n应该说：\n\n```text\nModule 持有 Wasm linear memory 的视图，使这块 Wasm heap 在 retainer path 中可见。\n```\n\n---\n\n### 8.3 `FS -> nameTable -> contents -> Uint8Array`\n\n专业含义：\n\n```text\nEmscripten FS 中某个文件节点持有文件内容。\n```\n\n可能是：\n\n```text\nUnity data package\n预加载资源\n临时文件\n缓存文件\n日志文件\n截图文件\n下载文件\n```\n\n排查方式：\n\n```text\n1. dump FS 文件路径；\n2. 打印文件 size；\n3. 读取文件头；\n4. hook FS.writeFile / FS.write；\n5. 结合日志和时间点。\n```\n\n---\n\n### 8.4 `Managed<wasm::NativeModule>`\n\n专业含义：\n\n```text\nJS 引擎中与 Wasm module 编译结果相关的 native 内部结构。\n```\n\n常见归属：\n\n```text\nWasm 代码编译产物\nWasm 函数元数据\nJIT / baseline / optimized code\n引擎内部缓存\n```\n\n通常不能通过业务 JS 直接释放。减少它的方式通常是：\n\n```text\n减小 Wasm 文件体积；\n裁剪未用代码；\n按需加载模块；\nWasm 分包；\n避免一次性加载全部功能。\n```\n\n---\n\n### 8.5 `BytecodeArray / FeedbackVector / SharedFunctionInfo`\n\n专业含义：\n\n```text\nJavaScript 引擎为 JS 代码生成的执行数据。\n```\n\n常见来源：\n\n```text\nUnity framework.js\n小游戏 runtime JS\n引擎 glue code\n业务 bundle\n插件 bundle\nBabel runtime\npolyfill\n```\n\n这类对象在 heap snapshot 里可能很多，但一般不是 20–50MB 驻留增长的主因，除非出现大量动态生成代码、eval、new Function 或反复加载 bundle。\n\n---\n\n### 8.6 `string` 大对象\n\n常见来源：\n\n```text\n源码字符串\n内联 source map\nbase64 图片\nbase64 音频\nJSON payload\n日志缓存\n错误堆栈\n压缩包文本\n```\n\n专业判断：\n\n```text\n源码字符串：\n  多数是运行时或打包成本，不一定是泄露。\n\nbase64：\n  高风险，因为体积比二进制更大，并且字符串在 JS heap 里很明显。\n\nJSON：\n  如果长期缓存大型上报包或配置包，可能泄露。\n\n日志：\n  如果数组无上限，会持续增长。\n```\n\n---\n\n## 9. Android 真机内存指标\n\n在微信小游戏真机排查中，JS heap snapshot 只覆盖一部分内存。Android 进程内存更适合用：\n\n```text\nadb shell dumpsys meminfo <pid>\n/proc/<pid>/smaps_rollup\n/proc/<pid>/status\nshowmap\nPerfetto\nheapprofd\nGPU/mtrack 指标\n```\n\nAndroid 官方文档说明 `dumpsys meminfo` 可用于查看进程内存，且通常关注 `Pss Total` 和 `Private Dirty`；`Private Dirty` 很重要，因为它是进程私有且不能轻易回收的脏页。([Android Developers][10]) Perfetto 文档也强调共享内存会映射到多个进程，PSS 会按比例分摊，因此分析时经常要关注 Private Dirty 来避免共享页干扰。([Android Git Repositories][11])\n\n### 9.1 RSS\n\n```text\nResident Set Size\n当前实际驻留在物理内存中的页总量。\n```\n\n特点：\n\n```text\n包含共享页。\n容易受系统缓存、共享库、其他进程影响。\n不能直接等同于“这个进程独占了多少内存”。\n```\n\n---\n\n### 9.2 PSS\n\n```text\nProportional Set Size\n按比例分摊共享页后的内存。\n```\n\n例如某个 100MB 共享库被 4 个进程共享，每个进程 PSS 可能只算 25MB。\n\n优点：\n\n```text\n适合估算进程对系统总内存的贡献。\n```\n\n缺点：\n\n```text\n共享页归属变化会造成波动。\n跨运行比较时容易不稳定。\n```\n\n---\n\n### 9.3 Private Dirty\n\n```text\n进程私有、已修改、不能直接丢弃的内存页。\n```\n\n这是判断真实驻留增长的重要指标。\n\n如果某阶段：\n\n```text\nPrivate Dirty +30MB\nRssAnon +30MB\n持续 60 秒不回落\n```\n\n这比单纯 `PSS +30MB` 更能证明有真实私有内存增长。\n\n---\n\n### 9.4 RssAnon / RssFile / RssShmem\n\n来自 `/proc/<pid>/status`。\n\n```text\nRssAnon\n  匿名内存，通常对应 native heap、Wasm memory、malloc、匿名 mmap。\n\nRssFile\n  file-backed mapping，例如 so、dex、wasm 文件、资源文件映射。\n\nRssShmem\n  共享内存，例如图形 buffer、ashmem、共享映射。\n```\n\n判断：\n\n```text\nRssAnon 增长：\n  更像 malloc / Wasm heap / native heap。\n\nRssFile 增长：\n  更像文件映射、代码、资源、cache。\n\nRssShmem / Graphics 增长：\n  更像图形缓冲、Canvas、GPU、Surface。\n```\n\n---\n\n### 9.5 dumpsys meminfo 常见分类\n\n| 分类                     | 通常含义                                           |\n| ---------------------- | ---------------------------------------------- |\n| Java Heap              | Java/Kotlin 对象，微信宿主或插件 Java 层                  |\n| Native Heap            | C/C++ malloc/new                               |\n| Code                   | so、dex、oat、jit code、wasm/code mapping          |\n| Stack                  | 线程栈                                            |\n| Graphics               | 图形相关内存                                         |\n| GL mtrack / EGL mtrack | OpenGL/EGL 资源                                  |\n| Private Other          | 不容易归类的 native/private memory，WebGL/XWeb/宿主内部常见 |\n| System                 | 系统归属或分摊内存                                      |\n\n微信小游戏中，`Private Other` 增长并不罕见，因为 WebGL、XWeb、Canvas、宿主桥接、资源缓存都可能落在这里。\n\n---\n\n## 10. 为什么“启动前 700MB，另一次启动前 800MB”很常见\n\nWebGL 小游戏内存基线波动大，常见原因：\n\n```text\n1. 微信多进程状态不同；\n2. appbrand 进程复用；\n3. 上一次小游戏残留缓存；\n4. XWeb / JS 引擎 code cache；\n5. Wasm 编译缓存；\n6. 资源缓存；\n7. GPU / Canvas / Surface 状态不同；\n8. Android 系统内存压力不同；\n9. PSS 共享页分摊变化；\n10. 是否已加载 Unity datapackage；\n11. 是否已触发音频、字体、图片、网络、WebGL context。\n```\n\n因此，专业测试口径应是：\n\n```text\n同一进程、同一次运行、同一阶段、稳定后采样。\n```\n\n不要比较：\n\n```text\nRun A 初始 700MB\nRun B 初始 800MB\n```\n\n而要比较：\n\n```text\n同一个 PID：\n  T0: WebGL/Unity 加载完成，目标模块未启动\n  T1: 目标模块启动后\n  T2: 首次功能触发后\n  T3: 清理后\n```\n\n---\n\n## 11. 内存增长、驻留、泄露的专业定义\n\n### 11.1 峰值内存\n\n```text\n某个操作期间短时间增加，随后回落。\n```\n\n例如：\n\n```text\n下载资源时 response buffer + 解压 buffer 同时存在。\n截图时 readPixels buffer + encode buffer 同时存在。\n上传时 body copy + native copy 同时存在。\n```\n\n峰值问题会导致 OOM，但不是泄露。\n\n---\n\n### 11.2 驻留内存\n\n```text\n操作完成后仍然保留，并且长期稳定存在。\n```\n\n例如：\n\n```text\n初始化后的 WebGL texture cache\nWasm heap 扩容后的容量\n引擎单例缓存\n常驻资源\n全局 buffer pool\n```\n\n驻留不一定是 bug，但需要判断是否必要、是否过早、是否过大。\n\n---\n\n### 11.3 泄露\n\n```text\n重复执行同一操作后，稳定基线持续上升，且没有释放路径。\n```\n\n例如：\n\n```text\n每次截图后多一个未 delete 的 texture；\n每次请求后 pending map 少删一个 entry；\n每次 start 后多注册一组事件监听；\n每次写 FS 临时文件后未 unlink；\n每次 push 日志无上限。\n```\n\n证明泄露要看趋势，不看单次。\n\n---\n\n## 12. WebGL 应用内存分析方法\n\n### 12.1 分阶段基线法\n\n推荐阶段：\n\n```text\nT0: 宿主进程已稳定，游戏未加载\nT1: WebGL/Unity runtime 加载完成\nT2: data package / 首包资源加载完成\nT3: 主场景进入，静置 60 秒\nT4: 目标功能初始化前\nT5: 目标功能初始化后\nT6: 首次功能调用后\nT7: 功能完成后静置 60 秒\nT8: stop / destroy / cleanup 后\nT9: 重复 N 次后\n```\n\n每个阶段采集：\n\n```text\nJS heap snapshot\nHEAPU8.buffer.byteLength\nFS 大文件列表\nWebGL 资源统计\ndumpsys meminfo\nsmaps_rollup\nlog marker\n```\n\n---\n\n### 12.2 JS Heap Snapshot 分析步骤\n\n步骤：\n\n```text\n1. 按 retained size 排序；\n2. 找大对象；\n3. 区分 wrapper 和 backing store；\n4. 展开 retainer path；\n5. 判断对象类别：\n   Wasm heap？\n   FS file？\n   network buffer？\n   WebGL readback？\n   string/base64？\n   closure queue？\n6. 做前后 snapshot diff；\n7. 对无法归因的大 buffer 加分配栈或 tag。\n```\n\n重点：\n\n```text\nRetainer path 回答“为什么还活着”。\nAllocation instrumentation 回答“哪里分配的”。\nSnapshot diff 回答“阶段之间新增了什么”。\n```\n\n---\n\n### 12.3 Android 真机分析步骤\n\n推荐命令：\n\n```bash\nadb shell \"ps -A | grep -i com.tencent.mm\"\n```\n\n找到小游戏进程，例如：\n\n```text\ncom.tencent.mm:appbrand0\ncom.tencent.mm:appbrand1\ncom.tencent.mm:tools\ncom.tencent.mm:toolsmp\n```\n\n然后采样：\n\n```bash\nadb shell \"PID=<pid>; while true; do \\\necho '===== MEM '$(date +%s)' pid='$PID' ====='; \\\ndumpsys meminfo $PID | sed -n '1,120p'; \\\necho '----- smaps_rollup -----'; \\\ncat /proc/$PID/smaps_rollup 2>/dev/null | grep -E 'Rss:|Pss:|Private_Clean:|Private_Dirty:|SwapPss:'; \\\necho '----- status -----'; \\\ncat /proc/$PID/status 2>/dev/null | grep -E 'VmRSS|VmHWM|RssAnon|RssFile|RssShmem|VmSwap'; \\\nsleep 2; \\\ndone\"\n```\n\n分析顺序：\n\n```text\n1. 先看同 PID；\n2. 再看所有微信相关进程合计；\n3. 优先看 Private Dirty、RssAnon；\n4. 再看 PSS、Graphics、Private Other；\n5. 结合 log marker 对齐操作阶段。\n```\n\n---\n\n### 12.4 Perfetto / heapprofd\n\nPerfetto 的 native heap profiling 可以按 malloc/free 调用栈聚合内存，但它只能看到 profiling 开始后的 native 分配，并且目标进程需要满足设备和权限条件。([Android Git Repositories][11])\n\n在微信小游戏场景下限制较多：\n\n```text\n1. 目标进程是微信，不是自己的 APK；\n2. 普通 user 设备可能不能 profile 微信进程；\n3. userdebug/root 设备更适合；\n4. 仍需结合业务 marker 才能知道哪个阶段触发。\n```\n\n可用时，Perfetto 适合回答：\n\n```text\nNative heap 增长到底来自哪个 native 调用栈？\n是 malloc？\n是 mmap？\n是图形资源？\n是线程栈？\n是 JIT/code？\n```\n\n---\n\n## 13. 常见高风险内存路径\n\n### 13.1 截图 / readPixels\n\n典型路径：\n\n```text\nWebGL framebuffer\n  -> readPixels\n  -> JS Uint8Array\n  -> Wasm encoder input\n  -> JPEG/PNG output\n  -> upload body\n```\n\n高风险点：\n\n```text\n1. 每次 new Uint8Array(width × height × 4)；\n2. 多个 readback ring slot；\n3. raw RGBA 同时存在 JS + Wasm + FS 三份；\n4. 使用 canvas.toTempFilePath / readFile 产生宿主内部 copy；\n5. base64 导出；\n6. 上传回调闭包持有大 buffer；\n7. GL texture/FBO/PBO 没有 delete。\n```\n\n优化：\n\n```text\n1. readback buffer pool；\n2. 限制 ring slot 数量；\n3. 直接写入 Wasm HEAPU8.subarray；\n4. 避免 raw RGBA 落 FS；\n5. 上传完成后清理引用；\n6. 显式 delete WebGL resources。\n```\n\n---\n\n### 13.2 WebGL texture / framebuffer\n\n危险模式：\n\n```js\nconst tex = gl.createTexture();\nconst fbo = gl.createFramebuffer();\n// 使用后没有 gl.deleteTexture(tex)\n// 使用后没有 gl.deleteFramebuffer(fbo)\n```\n\n专业建议：\n\n```text\n所有 create* 都应有对应 delete*。\n纹理缓存要有容量上限。\n场景切换、功能关闭、Canvas 重建时要释放 GPU 资源。\n```\n\n---\n\n### 13.3 FS 文件缓存\n\n危险模式：\n\n```js\nFS.writeFile(\"/tmp/large.raw\", rawBytes);\n// 忘记 FS.unlink\n```\n\n结果：\n\n```text\nFS.nameTable\n  -> FSNode.contents\n    -> Uint8Array\n      -> ArrayBuffer\n```\n\n优化：\n\n```text\n1. 临时文件用完 unlink；\n2. 不把 raw 大文件写入 FS；\n3. 对 FS 文件做 size dump；\n4. 对 FS.writeFile / FS.write 加 hook；\n5. 避免在小游戏中滥用 Emscripten FS cache。\n```\n\n---\n\n### 13.4 网络 response / upload body\n\n危险模式：\n\n```text\nresponse ArrayBuffer\n  -> Uint8Array copy\n  -> Wasm heap copy\n  -> FS copy\n  -> request body copy\n```\n\n优化：\n\n```text\n1. 使用 view / subarray，避免 slice 复制；\n2. 限制同时 inflight 请求；\n3. 上传完成后释放 body 引用；\n4. 不缓存完整历史包；\n5. 大文件流式处理或分块处理。\n```\n\n---\n\n### 13.5 Base64\n\n危险模式：\n\n```text\nbinary image\n  -> base64 string\n  -> JSON\n  -> upload\n```\n\n问题：\n\n```text\n1. base64 体积膨胀；\n2. 字符串在 JS heap 中明显变大；\n3. JSON stringify 可能再产生副本；\n4. 上传时可能再复制。\n```\n\n优化：\n\n```text\n优先使用 ArrayBuffer / Uint8Array / Blob-like binary API。\n```\n\n---\n\n### 13.6 事件监听和定时器\n\n危险模式：\n\n```js\nsetInterval(...)\nrequestAnimationFrame(loop)\nwx.onShow(callback)\nwx.onHide(callback)\ncanvas.addEventListener(...)\n```\n\n重复注册但不注销，会导致：\n\n```text\nclosure retained\ncontext retained\nlarge state retained\nmodule retained\nbuffer retained\n```\n\n优化：\n\n```text\n1. start/stop 成对；\n2. on/off 成对；\n3. timer id 统一管理；\n4. 多次 start 前先检查是否已经注册；\n5. stop 后断开大对象引用。\n```\n\n---\n\n## 14. 内存快照快速判断表\n\n| 快照表现                                        | 专业含义                            | 下一步                            |\n| ------------------------------------------- | ------------------------------- | ------------------------------ |\n| `HEAPU8 -> ArrayBuffer 256MB`               | Wasm linear memory / Unity Heap | 看是否启动前已存在，是否 grow              |\n| `FS -> nameTable -> contents -> Uint8Array` | Emscripten FS 文件内容              | dump 路径、文件头、写入栈                |\n| `system / JSArrayBufferData` 很大             | ArrayBuffer backing store       | 找 owner ArrayBuffer 和 retainer |\n| `Managed<wasm::NativeModule>` 很大            | Wasm 编译产物                       | 减小 wasm、分包、懒加载                 |\n| `WasmDispatchTable / Table`                 | Wasm 函数表                        | 通常不是泄露                         |\n| 大 `string`                                  | 源码、base64、JSON、日志               | 看内容和 retainer                  |\n| `system / Context` 很多                       | 闭包上下文                           | 查事件、timer、Promise、模块缓存         |\n| `Object retained 很大`                        | 容器支配子图                          | 展开子节点，不要误判                     |\n| `WeakMap table`                             | 弱映射内部结构                         | 注意 key/value 是否仍被强引用           |\n| JS heap 无增长但 Android 内存涨                    | native/GPU/mmap/宿主缓存            | 看 meminfo、smaps、Perfetto       |\n\n---\n\n## 15. 优化原则\n\n### 15.1 延迟初始化\n\n不要在启动阶段初始化所有重资源。\n\n```text\n启动阶段只初始化核心逻辑。\n截图、编码、上传、音频、复杂 WebGL FBO、离屏 Canvas 应按需初始化。\n```\n\n---\n\n### 15.2 复用 buffer\n\n适合复用：\n\n```text\nreadPixels buffer\nYUV buffer\nencode output buffer\nnetwork body buffer\ndecompression scratch\ntile buffer\ntemporary typed arrays\n```\n\n注意：\n\n```text\n复用不是无限缓存。\nbuffer pool 必须有最大容量和释放时机。\n```\n\n---\n\n### 15.3 避免重复副本\n\n优先：\n\n```js\nview = HEAPU8.subarray(ptr, ptr + len);\n```\n\n谨慎：\n\n```js\narray.slice()\nbuffer.slice()\nArray.from()\nJSON.stringify(largeObject)\nbase64 encode\nFS.readFile()\nFS.writeFile()\n```\n\n---\n\n### 15.4 显式释放 WebGL 资源\n\n```js\ngl.deleteTexture(tex);\ngl.deleteBuffer(buf);\ngl.deleteFramebuffer(fbo);\ngl.deleteRenderbuffer(rbo);\ngl.deleteProgram(program);\ngl.deleteShader(shader);\n```\n\n同时清空 JS 引用：\n\n```js\ntex = null;\nfbo = null;\n```\n\n---\n\n### 15.5 控制 Emscripten FS\n\n```text\n临时文件：\n  用完 unlink。\n\n大 raw 数据：\n  尽量不进 FS。\n\n缓存文件：\n  有容量限制和淘汰策略。\n\n持久化：\n  明确 sync 时机和内存副本成本。\n```\n\n---\n\n### 15.6 控制 Wasm heap 峰值\n\n```text\n1. 避免同一时间保留多个大 vector/string/buffer；\n2. 分块处理大资源；\n3. 降低启动阶段 malloc 峰值；\n4. 调整 INITIAL_MEMORY / MAXIMUM_MEMORY；\n5. 避免一次操作把 heap grow 到高水位；\n6. 使用引擎 profiler 或 allocator 统计确认真实使用量。\n```\n\n---\n\n### 15.7 用阶段差分而不是单点比较\n\n专业测试报告应该写：\n\n```text\n同一 PID 下，T0 到 T1：\nPrivate Dirty 中位数 +X MB；\nRssAnon 中位数 +Y MB；\nJS heap retained +Z MB；\nHEAPU8.buffer 未变化；\nFS 大文件未新增；\nGraphics +N MB；\n持续 60 秒未回落。\n```\n\n不要写：\n\n```text\n这次启动前 700MB，另一次 800MB，所以 SDK/模块涨了 100MB。\n```\n\n---\n\n## 16. 推荐的分析流程模板\n\n### 16.1 证明是否增长\n\n```text\n1. 选定同一设备、同一微信版本、同一小游戏包；\n2. 冷启动微信或清理 appbrand 进程；\n3. 找到目标 PID；\n4. 进入游戏并等待稳定；\n5. 打 T0 marker；\n6. 执行目标操作；\n7. 打 T1/T2/T3 marker；\n8. 每阶段等待 60 秒；\n9. 采集 meminfo + smaps + heap snapshot；\n10. 至少重复 5 次，取中位数。\n```\n\n---\n\n### 16.2 证明是否泄露\n\n```text\nfor i in 1..10:\n  start feature\n  wait stable\n  stop feature\n  wait stable\n  collect memory\n```\n\n判断：\n\n```text\n每轮结束后的稳定基线持续上升：\n  泄露或未释放缓存。\n\n第一轮上升后稳定：\n  驻留成本。\n\n操作期间上升，结束后回落：\n  峰值成本。\n```\n\n---\n\n### 16.3 归因路径\n\n```text\nJS heap 增长：\n  heap snapshot / retainer / allocation stack。\n\nWasm heap capacity 增长：\n  HEAPU8.buffer.byteLength / sbrk / malloc stats。\n\nFS 增长：\n  FS dump / file path / file head / FS.write hook。\n\nGPU/Graphics 增长：\n  WebGL resource counter / delete audit / dumpsys Graphics / GL mtrack。\n\nNative heap 增长：\n  dumpsys Native Heap / smaps RssAnon / Perfetto heapprofd。\n\nPrivate Other 增长：\n  XWeb/宿主/图形/匿名 mmap，需要 marker + Perfetto + 功能开关实验。\n```\n\n---\n\n## 17. 标准术语表\n\n| 术语                       | 专业解释                                     |\n| ------------------------ | ---------------------------------------- |\n| JS Heap                  | JavaScript 引擎管理的对象堆                      |\n| GC Root                  | 垃圾回收根对象，从它可达的对象不会被回收                     |\n| Shallow Size / Self Size | 对象自身大小                                   |\n| Retained Size            | 对象释放后可能连带释放的大小                           |\n| Retainer Path            | 当前是谁持有该对象                                |\n| Allocation Stack         | 对象创建时的调用栈                                |\n| ArrayBuffer              | JS 二进制缓冲区对象                              |\n| JSArrayBufferData        | ArrayBuffer 的 native backing store       |\n| TypedArray               | Uint8Array、Float32Array 等 ArrayBuffer 视图 |\n| WebAssembly.Memory       | Wasm 线性内存对象                              |\n| HEAPU8                   | Emscripten 对 Wasm memory 的 Uint8Array 视图 |\n| Module                   | Emscripten runtime 全局对象                  |\n| MEMFS                    | Emscripten 内存文件系统                        |\n| IDBFS                    | IndexedDB-backed Emscripten 文件系统         |\n| FSNode.contents          | Emscripten FS 文件内容                       |\n| Wasm NativeModule        | JS 引擎中的 Wasm 编译产物                        |\n| WebGLTexture             | WebGL 纹理 wrapper                         |\n| Framebuffer              | 离屏或目标渲染缓冲组合                              |\n| Renderbuffer             | 深度/模板/多采样渲染缓冲                            |\n| PBO                      | Pixel Buffer Object，用于像素传输               |\n| PSS                      | 共享页按比例分摊后的进程内存                           |\n| RSS                      | 进程驻留物理内存                                 |\n| Private Dirty            | 进程私有且不可直接丢弃的脏页                           |\n| RssAnon                  | 匿名驻留内存，常对应 malloc/Wasm/native            |\n| RssFile                  | 文件映射驻留内存                                 |\n| RssShmem                 | 共享内存驻留                                   |\n| Graphics / GL mtrack     | 图形/GPU/EGL/OpenGL 相关内存                   |\n| Private Other            | Android meminfo 中不易归类的私有内存               |\n\n---\n\n## 18. 最重要的心智模型\n\n分析 WebGL / 微信小游戏内存时，应该始终把内存分成四个层面：\n\n```text\n1. JS 可见对象\n   Object / Array / Closure / String / ArrayBuffer wrapper。\n\n2. JS 外部 backing store\n   system / JSArrayBufferData，大型二进制数据。\n\n3. Wasm / Emscripten / Unity runtime\n   HEAPU8、Unity Heap、FS、Wasm compiled module、Table。\n\n4. 宿主和系统层\n   WebGL/GPU、Canvas、XWeb、微信小游戏 runtime、Android native heap、Graphics、Private Other。\n```\n\n一个 solid 的内存结论必须回答：\n\n```text\n1. 增长发生在哪个阶段？\n2. 增长属于 JS heap、Wasm heap、FS、GPU、native，还是宿主缓存？\n3. 是峰值、驻留，还是泄露？\n4. 是同一进程内稳定复现，还是跨运行波动？\n5. 快照中的 retainer path 证明了什么？\n6. allocation stack 或打点是否证明了分配来源？\n7. 这块内存对功能是否必要？\n8. 是否过早分配？\n9. 是否可以延迟、复用、缩容或释放？\n```\n\n最终，专业表述应从：\n\n```text\n“启动后内存涨了 50MB”\n```\n\n升级为：\n\n```text\n“在同一 appbrand 进程内，目标功能启动后 60 秒稳定区间，Private Dirty 中位数增加 28MB，RssAnon 增加 31MB；JS heap snapshot 未新增大型 retained object，HEAPU8.buffer 未增长，FS 大文件未新增；因此该增长更可能位于 native / graphics / 宿主 runtime 层。下一步应按 WebGL resource、Canvas readback、网络 body、native heap 四类路径继续分解。”\n```\n\n这就是 WebGL / 微信小游戏内存分析中最重要的专业化思考方式。\n\n[1]: https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/framebufferTexture2D?utm_source=chatgpt.com \"WebGLRenderingContext: framebufferTexture2D() method\"\n[2]: https://docs.unity3d.com/cn/2020.1/Manual/webgl-gettingstarted.html?utm_source=chatgpt.com \"WebGL 开发入门\"\n[3]: https://docs.unity3d.com/Manual/webgl-building.html?utm_source=chatgpt.com \"Web Build folder\"\n[4]: https://docs.unity3d.com/6000.3/Documentation/Manual/webgl-memory.html?utm_source=chatgpt.com \"Memory in Unity Web\"\n[5]: https://emscripten.org/docs/porting/files/file_systems_overview.html?utm_source=chatgpt.com \"File System Overview\"\n[6]: https://emscripten.org/docs/api_reference/Filesystem-API.html?utm_source=chatgpt.com \"File System API\"\n[7]: https://docs.cocos.com/creator/3.6/manual/zh/editor/publish/publish-wechatgame.html?utm_source=chatgpt.com \"发布到微信小游戏\"\n[8]: https://docs.unity.cn/cn/tuanjiemanual/Manual/WebGLPlatform.html?utm_source=chatgpt.com \"团结引擎- 手册: 平台特征简介\"\n[9]: https://developer.chrome.com/docs/devtools/memory-problems/heap-snapshots?utm_source=chatgpt.com \"Record heap snapshots | Chrome DevTools\"\n[10]: https://developer.android.com/tools/dumpsys?utm_source=chatgpt.com \"dumpsys | Android Studio\"\n[11]: https://android.googlesource.com/platform/external/perfetto/%2B/refs/heads/main/docs/case-studies/memory.md?utm_source=chatgpt.com \"Debugging memory usage on Android\"\n"
}
