性能与调试
把脚本跑得又快又稳:热路径守则、内存卫生,以及出问题时看哪里。
性能模型
每帧每实例的成本结构:
| 环节 | 成本 |
|---|---|
| 快照搬运(宿主 → 模块) | 唯一强制的 Java 穿越:宿主用复用构建器把 POD 快照写进共享页 / 直接缓冲 |
数据读取(Vehicle / Pids / …) | 普通内存读,零成本 |
| 绘制记录写入 | 内存区内 bump 分配,零分配零装箱 |
像素光栅(Gfx2D) | 纯 C++ 循环;src-over 不透明快速路径 |
| 宿主回放 | 与 JS 相同的渲染路径;纹理上传只搬运脏矩形 |
Api 调用 | JSON 编码 + 反射 + JSON 解码往返——昂贵 只作低频通道 |
热路径守则
- render 里不碰
Api。高频数据全在快照里;高频绘制全走记录流。JS 时代的周期性 GC 卡顿正是「每帧每车一次反射调用」造成的。 - 资源只在
create建:模型、纹理、Java 对象句柄、回调——建好存State,render只用。 - 避免每帧资源读取 / HTTP:网络进
BackgroundWorker,结果进你自己的同步缓存,render 读缓存。 - 纹理只在内容变化时上传:静态背景画一次;动态内容用
push_clip限定重画范围,让脏矩形只覆盖真正变化的条带。 - 逐对象错相:多实例轮播 / 闪烁不要同帧齐动——用相位错开的调度让脏区分散到不同帧(
in_game_time()或game_time_millis()做时钟)。 - 字符串临时对象节制:
StrView比较免拷贝;确实要拼接时注意std::string的分配(短串 SSO 免分配)。
内存与延迟回收
宿主侧的内存纪律(脚本作者了解即可,SDK 自动受益):
- 五个输出内存区每模块一份、帧间复用;像素内存区初始 1 MB、按需增长(上限 16 MB)、尖峰过后确定性收缩回下限——不再有每模块 16 MB 的固定驻留。
- 实例离开视锥不销毁(状态原样保留、重入镜零重建);真正离开客户端车辆列表约 5 秒后才回收。
- 大块释放走
defer_free(ABI 10):在render/dispose里死掉的兆级缓冲交给宿主后台回收线程,避免把free的全部成本记到当前帧、并放大 GC 安全点等待。契约:块必须已死、无析构、此后无线程(含宿主回放)再触碰。SDK 的像素内存区收缩已自动遵守;你自己的大缓冲按同样规则调用host->defer_free,宿主过旧(槽位为 null)时同步 free 即可。
观测与日志
| 看什么 | 在哪 | 内容 |
|---|---|---|
| 模块 census | logs/latest.log 启动段 | 已加载模块数与类型;传输通道摘要行(含 transport: 字样:shared-memory / panama-ffm 等)——确认 shm / FFM 真正生效、是否静默降级 |
| 加载失败原因 | census 行 + 调试模式 | 某侧(JS 或 C++)缺失 / 加载失败只跳过该侧并留提示,另一侧照常工作 |
| 崩溃防护 | latest.log | native script faulted: code=… module disabled——模块被禁用的精确原因 |
| 清扫流量 | latest.log(每分钟一行) | 实例销毁节奏的聚合日志,可直接观测实例寿命行为 |
| 上传字节数 | GraphicsTexture::last_upload_bytes() | 你的代码里打点:脏矩形策略是否奏效一目了然 |
改完库按 F3+T(或模组的「重载全部 MTR 脚本」快捷键)即可重载 C++ 脚本——卸载与重新发现走完整路径,无需重启游戏。
JVM 参数
| 参数 | 默认 | 作用 |
|---|---|---|
-Djcm.nativeIdleMillis | 90000 | 实例仍存在但长期未渲染时,超过该毫秒数才释放;0 = 不按时限释放 |
-Djcm.nativeIdleMax | 32 | 热实例上限,超出按最久未渲染者优先释放;0 = 不设上限 |
-Djcm.ffm.disable | 关 | 调试开关:禁用 Panama FFM 加载路径 |
实例回收与销毁还有每 tick 预算(默认 2 个、旧者先出)与世界卸载 / 切维度 / 断线时的一次性冲刷。
常见问题
模块完全不加载?
- 确认用的是 Release 或 Complete Build jar——快速 CI 产物不含原生桥,日志会写
native bridge unavailable(这不是你的模块的问题)。 - 查平台目录:当前系统只会加载对应平台的库;无扩展名 stem 声明会自动展开为各平台路径(见打包)。
- 查
ID与声明条目的id是否逐字一致。 - 查 ABI:宿主接受 6–11;ABI 8+ 库需要 2.3.0-beta-2.yanyang.7 及以上宿主。
屏幕出现占位方框(tofu)?
CJK 字形在宿主字体不覆盖该码点时退化——给 Text::font() / Gfx2D 构造传资源字体 id(如 jsblock:shanghai),或检查所用字体集是否包含这些字符。
偶发掉帧?
- 先看是不是「每帧整屏重画」:静态内容画一次、动态内容 clip 条带重画,用
last_upload_bytes()验证。 - 再看是不是多实例同帧齐动:轮播 / 闪烁 / 开门动画逐对象错相。
- 最后查有没有 render 里的
Api调用(包括间接的:ctx.set_auto_z_ordering等个别上下文方法在 ABI 8+ 会转发一次)。
Windows 上行为和别处不一样?
ABI 6 之前的历史版本曾依赖「零填充内存可直接当对象用」——这恰好只在 MSVC 布局下幸存。当前 SDK 的 mtrInit() 契约已根治;如果你在写自定义宿主,务必在分配状态块后调用 mtrInit()。
回到:文档首页。