前言
J2ME (CLDC/MIDP) 運行環境極度受限:JAR 包大小限制 (早期 64KB/128KB)、堆內存 1-2 MHz CPU、無 JIT 編譯器、垃圾回收器簡陋。優化不是可選項,是生存法則。本文匯總 20 條實戰驗證的優化鐵律,分為**常規優化** (遵循 OO、可維護) 與**極限優化** (犧牲可讀性、突破極限) 兩類。
常規優化 (推薦所有項目執行)
- 幀率實測平衡:開發初期測試目標設備最大穩定 FPS,在流暢度與功能豐富度間找平衡點。
- Graphics 對象復用:
GameCanvas.getGraphics() 每次創建新對象,**僅在 paint() 參數中獲取一次**,全幀復用。
- 監聽器合併:主 MIDlet 實現
CommandListener、ItemStateListener 等多介面,避免匿名內部類產生大量微小 class 文件。
- 廠商 SDK 優先:Nokia UI API、Siemens Game API、Motorola 專有擴展往往比標準 MIDP 實現更高效 (特別是圖形/音頻)。
- Profiler 定位瓶頸:WTK Profiler、Nokia Energy Profiler、Sony Ericsson Profiler 精準定位熱點方法、內存分配熱點、GC 頻率。
- 慎用 System.gc():非阻塞時調用可緩解內存壓力,但 Nokia 等廠商不推薦,低端機執行極慢,**僅在場景切換等明確閒置點調用**。
- 陣列替代 Vector:
Vector 同步開銷大、擴容複製頻繁、泛型擦除需轉型。固定大小或可預估上限場景**強制使用基本類型陣列或對象陣列**。
- 圖片規格匹配:根據目標設備螢幕分辨率/色深製作圖片,避免運行時縮放/色深轉換開銷;使用 PNG-8 替代 PNG-24,去除 Alpha 通道 (若不需要透明)。
- 廢棄對象置 null:循環體中大對象 (圖片、字節數組) 使用完畢顯式置 null,幫助 GC 更早回收 (簡單 GC 無分代掃描全堆)。
- 混淆器雙重收益:ProGuard / RetroGuard 不僅防反編譯,**顯著減小 class 文件體積** (短名替換、刪除未用代碼、優化字節碼),間接提升類加載速度。
數據存儲與加載優化
- 大數據編碼/外部化:關卡數據、地圖、文本過大時,編碼為字符串 (Base64/自定義壓縮) 嵌入代碼,或存為二進制資源文件 (
getResourceAsStream 讀取),避免類初始化時佔用大量常量池。
- 內建集合優先:CLDC 無 Collections Framework,**直接使用
Hashtable、Vector**,避免第三方集合庫增加體積。
- 資源文件合包:多個小圖片/音頻/數據文件打包為單一二進制大文件,運行時解析偏移量讀取,減少 JAR 條目數、類加載器開銷、文件系統 IO。
- 圖集技術:所有小圖片合併為一張大圖集,運行時
Graphics.drawRegion() 切片繪製,**極大減少圖片解碼次數、文件句柄、內存碎片**。
極限優化 (僅在 JAR/內存/CPU 極度受限時啟用,破壞 OO、降低可維護性)
- 受控面向對象:減少虛方法調用 (動態分發開銷)、扁平化繼承體系、合併微小類、**慎用介面** (介面調用比類調用慢)。
- 最小化類數量:每個 class 文件有固定開銷 (常量池、方法表、屬性表);合併邏輯相關類,目標
< 100 classes (視複雜度)。
- 最小化方法數量:內聯熱點邏輯;調用開銷排序:**同步方法 > 介面方法 > 實例方法 > final 方法 > 靜態方法**;熱點路徑改為
static final 方法。
- final static 鎖定熱點:關鍵工具方法標記
static final,編譯器可內聯,JVM 可常量折疊。
- public 字段替代 Getter/Setter:直接訪問字段消除方法調用開銷,**僅限內部邏輯、非 API 邊界、線程安全已驗證場景**。
- 循環展開與強度削減:手動展開關鍵循環 (如渲染循環)、乘法轉位移、預計算常量提取循環外。
- 避免異常控制流:異常創建/堆棧追蹤極其昂貴,**絕不**用於正常業務流程控制 (如遍歷結束判斷)。
驗證與回歸機制
- 建立**自動化性能基準測試** (關鍵場景幀率、內存峰值、啟動時間),每次優化後跑分對比。
- **真機測試為準**,模擬器性能特徵與實機差異巨大 (尤其是 GC、圖形管線、按鍵響應)。
- 針對主流目標機型 (按市場佔有率 Top 5) 建立專項測試矩陣。
「優化必須建立在目標設備規格之上。以上方法不一定在所有 MIDP 設備生效,不一定適合每個項目,總之,應根據自身情況權衡。」