经纬志
首页 / 实战手册 / deck.gl 可视化

一百万条轨迹,页面直接崩了

先看结论

CSV 解析完内存就到 2.4 GB,浏览器标签页变白,标题变成“页面无响应”。

最后验证 2026-11-03 · 多技术栈 · 更新 2026-10-29 · 引用本文

现象

加载 120 万条 GPS 记录后,控制台报内存分配失败,页面崩溃:

Uncaught RangeError: Array buffer allocation failed
    at new Float32Array (<anonymous>)
    at TripsLayer.initializeState (trips-layer.js:88)

内存面板里 JS 堆持续上涨,GC 后仍不回落,说明数据被长期持有。

原因

用 d3.csv 或 papaparse 解析后得到的是对象数组, 每行对象连同属性名、字符串值大约 200–400 字节。120 万行就是 400 MB 以上。 deck.gl 在更新时还会保留上一份数据做差分,峰值接近两倍。

TripsLayer 的 getPath 返回普通数组, 每个点再创建一个小数组对象,又叠加一轮开销。

排查过程

  1. 在 onDataLoad 里打印记录数与内存占用。
  2. 用 performance.memory.usedJSHeapSize 对比解析前后。
  3. 只保留 10 万条做对照,确认崩溃点是数量级而不是某条脏数据。
console.log('记录数', data.length);
console.log('堆内存 MB', (performance.memory.usedJSHeapSize / 1048576).toFixed(1));

解决方案

方案一:改用二进制属性,把经纬度、时间、颜色压进 Float32Array。 同样一百万条,内存从 400 MB 降到几十 MB:

var count = 1200000;
var positions = new Float32Array(count * 2);   // [lng, lat]
var timestamps = new Float32Array(count);

for (var i = 0; i < count; i++) {
  positions[i * 2] = rows[i][0];
  positions[i * 2 + 1] = rows[i][1];
  timestamps[i] = rows[i][2];
}

var layer = new deck.ScatterplotLayer({
  id: 'gps',
  data: {
    length: count,
    attributes: {
      getPosition: { value: positions, size: 2 },
      getTimestamp: { value: timestamps, size: 1 }
    }
  },
  getRadius: 2,
  radiusUnits: 'pixels',
  getFillColor: [0, 160, 255]
});

方案二:按时间或空间分块,只加载当前视野与当前时段的数据。 这是数据量再大也能维持交互的做法:

function loadChunk(tileKey) {
  fetch('/api/gps?tile=' + tileKey)
    .then(function (r) { return r.arrayBuffer(); })
    .then(function (buf) {
      var positions = new Float32Array(buf);
      overlay.setProps({
        layers: [makeLayer(positions)]
      });
    });
}

map.on('moveend', function () {
  loadChunk(currentTileKey(map.getBounds()));
});

方案三:轨迹层改用预计算的分段数据,避免在访问器里临时拼数组:

new deck.TripsLayer({
  id: 'trips',
  data: binaryTrips,
  getPath: function (d) { return d.path; },   // 预先构造成扁平数组
  getTimestamps: function (d) { return d.timestamps; },
  getColor: [253, 128, 93],
  widthMinPixels: 2,
  trailLength: 180,
  currentTime: 3600
});

验证

加载同等数据后,堆内存稳定在 150 MB 以内,不再出现 Array buffer allocation failed。 缩放与旋转保持 30 fps 以上,切到别的时段再切回,内存不持续上涨。

先看数据表示:大部分“deck.gl 内存爆”不是渲染问题, 而是把 CSV 对象数组直接喂给图层。把对象数组换成 typed array, 通常一行改动就能解决大半。

引用本文

经纬志(2026)。《一百万条轨迹,页面直接崩了》。https://www.jingweizhi.com/cookbook/deckgl-oom-large-data.html(访问日期 2026-11-03)。