JNI 路线序列化:从逐对象创建到定长缓冲区
问题现场
路线对象由大量 link 组成。旧实现为每个 link 创建独立的序列化对象,并在写入过程中多次扩容数组。单次操作看起来很轻,但在长路线和频繁刷新场景中,对象分配、复制和 GC 成本会叠加。
定位方法
- 使用 Perfetto 火焰图确认耗时集中在 JNI 通信前的序列化阶段。
- 将业务阶段日志与 trace 时间段对齐,排除路线规划和网络解析。
- 对比不同 link 数量下的对象创建次数与耗时增长趋势。
改造
先遍历路线元数据,计算完整序列化结果所需的空间;随后一次性申请定长缓冲区,在同一块内存中连续写入所有 link。
const auto bytes = estimateRouteBytes(route);
std::vector<std::byte> buffer(bytes);
RouteWriter writer(buffer);
for (const auto& link : route.links) {
writer.write(link);
}
结果与边界
序列化阶段耗时下降约 90%。这并不意味着所有序列化都应该预扫描:当数据只能流式获得,或预估成本接近正式写入成本时,需要在峰值内存和吞吐之间重新权衡。