效能與除錯
把腳本跑得又快又穩:熱路徑守則、記憶體衛生,以及出問題時看哪裏。
效能模型
每幀每實例的成本結構:
| 環節 | 成本 |
|---|---|
| 快照搬運處理(宿主 → 模組) | 唯一強制的 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裏死掉的 MB 級緩衝區交給宿主背景回收執行緒,避免把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()。
回到:文件首頁。