性能与调试

性能模型

每帧每实例的成本结构:

环节成本
快照搬运(宿主 → 模块)唯一强制的 Java 穿越:宿主用复用构建器把 POD 快照写进共享页 / 直接缓冲
数据读取(Vehicle / Pids / …)普通内存读,零成本
绘制记录写入内存区内 bump 分配,零分配零装箱
像素光栅(Gfx2D)纯 C++ 循环;src-over 不透明快速路径
宿主回放与 JS 相同的渲染路径;纹理上传只搬运脏矩形
Api 调用JSON 编码 + 反射 + JSON 解码往返——昂贵 只作低频通道

热路径守则

  1. render 里不碰 Api。高频数据全在快照里;高频绘制全走记录流。JS 时代的周期性 GC 卡顿正是「每帧每车一次反射调用」造成的。
  2. 资源只在 create 建:模型、纹理、Java 对象句柄、回调——建好存 State,render 只用。
  3. 避免每帧资源读取 / HTTP:网络进 BackgroundWorker,结果进你自己的同步缓存,render 读缓存。
  4. 纹理只在内容变化时上传:静态背景画一次;动态内容用 push_clip 限定重画范围,让脏矩形只覆盖真正变化的条带。
  5. 逐对象错相:多实例轮播 / 闪烁不要同帧齐动——用相位错开的调度让脏区分散到不同帧(in_game_time() 或 game_time_millis() 做时钟)。
  6. 字符串临时对象节制: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 即可。

观测与日志

看什么在哪内容
模块 censuslogs/latest.log 启动段已加载模块数与类型;传输通道摘要行(含 transport: 字样:shared-memory / panama-ffm 等)——确认 shm / FFM 真正生效、是否静默降级
加载失败原因census 行 + 调试模式某侧(JS 或 C++)缺失 / 加载失败只跳过该侧并留提示,另一侧照常工作
崩溃防护latest.lognative script faulted: code=… module disabled——模块被禁用的精确原因
清扫流量latest.log(每分钟一行)实例销毁节奏的聚合日志,可直接观测实例寿命行为
上传字节数GraphicsTexture::last_upload_bytes()你的代码里打点:脏矩形策略是否奏效一目了然

改完库按 F3+T(或模组的「重载全部 MTR 脚本」快捷键)即可重载 C++ 脚本——卸载与重新发现走完整路径,无需重启游戏。

JVM 参数

参数默认作用
-Djcm.nativeIdleMillis90000实例仍存在但长期未渲染时,超过该毫秒数才释放;0 = 不按时限释放
-Djcm.nativeIdleMax32热实例上限,超出按最久未渲染者优先释放;0 = 不设上限
-Djcm.ffm.disable关调试开关:禁用 Panama FFM 加载路径

实例回收与销毁还有每 tick 预算(默认 2 个、旧者先出)与世界卸载 / 切维度 / 断线时的一次性冲刷。

常见问题

模块完全不加载?

  1. 确认用的是 Release 或 Complete Build jar——快速 CI 产物不含原生桥,日志会写 native bridge unavailable(这不是你的模块的问题)。
  2. 查平台目录:当前系统只会加载对应平台的库;无扩展名 stem 声明会自动展开为各平台路径(见打包)。
  3. 查 ID 与声明条目的 id 是否逐字一致。
  4. 查 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()。


回到:文档首页。