J2ME 堆記憶體 = 程式碼區(類字節碼、常量池)+ 資源區(Image、Player、byte[]、RMS 緩衝)+ 運行區(物件實例、棧幀)。
監測兩大法寶:
Runtime.getRuntime().freeMemory() 即時列印,集成引擎每幀輸出,精準定位峰值。| 策略 | 操作 | 收益 | 風險 |
|---|---|---|---|
| 合併類 | 將輔助類內嵌為內部類或合併至主類 | 減少 .class 檔案數、常量池重複 | 單類過大超過 64 KB 方法限制、載入超時 |
| 刪除調試碼 | 發布構建自動移除 try-catch、System.out.println、斷言 | 位元碼縮減 10-20% | 需保留行號表供崩潰定位 |
| 字串外部化 | 長字串、關卡數據存資源檔,運行時載入再釋放 | 常量池大幅瘦身 | I/O 峰值需配合 GC |
| 適度模組化 | 引擎核心 6-7 類,邏輯腳本外置 | 平衡載入速度與維護性 | — |
公式:記憶體 = 寬 × 高 × 像素位深/8
| 平台 | 色深 | 位深 | 例:176×208 |
|---|---|---|---|
| S40 (4096 色) | 12-bit | 1.5 bytes | 55 KB |
| S60 (65536 色) | 16-bit | 2 bytes | 73 KB |
| 高階 (24-bit) | 24-bit | 3 bytes | 110 KB |
錯誤迷思:壓縮 PNG(減小檔案大小)≠ 減少記憶體佔用。解碼後皆為原始像素陣列。
drawRegion 繪製,減少 Image 物件開銷(每個 ~200 bytes header)。// 關卡載入:僅載入本關敵人圖片
void loadLevel(int levelId) {
unloadAllImages(); // 釋放上一關
String[] needed = LEVEL_RES[levelId];
for (String path : needed) {
images.put(path, Image.createImage(path));
}
System.gc(); // 強制回收
}
案例:《火影武士》6 種怪物同時載入 OOM → 每關僅載入 2-3 種,完美通關老 40 機型。
| 操作 | 峰值特徵 | 對策 |
|---|---|---|
getResourceAsStream | 類載入器緩衝整個資源 | 分批讀取、避免大資源單次載入 |
RecordStore.openRecordStore | RMS 引擎建立索引緩衝 | 啟動時延遲打開、用完即關閉 |
Manager.createPlayer | 音訊解碼器分配緩衝區 | 音效預載入轉為 ByteArrayInputStream、背景音樂串流播放 |
Image.createImage | 解碼器臨時緩衝 + 目標像素陣列 | 錯開載入時機、載入前 System.gc() |
ByteArrayInputStream 載入避免檔案系統緩衝freeMemory() > 總堆 40%?否則精簡類/字串。new 次數 = 0?否則導入物件池。freeMemory() 恢復一致?否則有洩漏。J2ME 記憶體管理沒有捷徑,只有精算、監測、分級、釋放四字訣。將 Runtime.freeMemory() 刻入引擎核心,每次提交構建前跑全流程壓測,OutOfMemoryError 就會從「家常便飯」變成「歷史名詞」。