Java 開發者使用 SWT/JFace 時,無法完全依賴 JVM 垃圾回收釋放系統資源(JVM 僅能釋放記憶體中的資源句柄引用物件)。SWT 中系統資源基底類別 org.eclipse.swt.graphics.Resource 明確要求:「應用程式建立的資源必須自行 dispose」。這也是「誰建立,誰負責」原則的由來,適用於 Image、Font、Color、GC 等所有系統資源。
系統資源洩漏會導致 org.eclipse.swt.SWTError: No more handles,嚴重時造成應用程式崩潰,罪魁禍首往往是未正確釋放資源的程式碼。
org.eclipse.swt.graphics.Image 文件明確指出:「應用程式必須明確呼叫 Image.dispose() 釋放作業系統資源」。ImageData 可視為 Image 的中繼資料模型,描述建立 Image 所需資訊。
SWT 僅提供一條路徑:自行覆寫元件的 dispose() 方法釋放資源。若覺得不夠靈活,請看下一節 Display Hook 機制。
Display 提供 disposeExec(Runnable) 註冊自定義執行緒,在 Display.release() 時回呼:
display.disposeExec(new Runnable() {
public void run() {
image.dispose();
// 其他資源釋放邏輯
}
});
這正是 JFace ImageRegistry、ResourceManager 能確保 Display 釋放時徹底清理資源的基礎。
dispose():Display 運行期間即可釋放,最佳方式。JFace 提供 ImageDescriptor 工廠類別建立圖片,兩種方式:
createImage():每次建立全新 Image,呼叫者必須自行 dispose。createResource():使用快取復用機制,回傳共享 Image。完成使用後必須呼叫 destroyResource() 而非直接 dispose Image。差異分析:
createImage 每次建立新資源,耗效能。createResource 快取復用提升效能,但未解決釋放責任,反而易造成誤以為無需管理而洩漏。孤立使用 ImageDescriptor 時,開發者仍須自行管理釋放(覆寫元件 dispose 或 Display Hook)。
JFaceResources 為資源管理外觀類別,取得 ImageRegistry 時會自動向 Display 註冊 Hook:
public static ResourceManager getResources(final Display toQuery) {
ResourceManager reg = (ResourceManager) registries.get(toQuery);
if (reg == null) {
final DeviceResourceManager mgr = new DeviceResourceManager(toQuery);
toQuery.disposeExec(new Runnable() {
public void run() {
mgr.dispose();
registries.remove(toQuery);
}
});
}
return reg;
}
DeviceResourceManager 對 DeviceResourceDescriptor (ImageDescriptor) 進行引用計數管理:
create(descriptor):首次註冊建立引用計數並 allocate 資源快取;後續增加計數回傳快取資源。destroy(descriptor):計數歸 1 時徹底銷毀;大於 1 時遞減計數(資源保留)。put(String key, Image image) // 註冊 Image,自行管理引用計數
put(String key, ImageDescriptor descriptor) // 註冊 Descriptor,委託 ResourceManager 管理
Image get(String key)
ImageDescriptor getDescriptor(String key)
remove(String key) // 取消註冊
dispose() // 銷毀資源
結論:
ImageDescriptor 完全委託 ResourceManager 管理與銷毀(在 ResourceManager.dispose() 中)。Image 則由 ImageRegistry 以內部類 OriginalImageDescriptor 包裝管理引用計數,銷毀在自身 dispose() 完成(已 Hook 到 Display 釋放流程)。remove() 取消註冊即可。dispose()。Eclipse Workbench shared images 即採此模式。外掛暴露圖片資源供其他外掛使用:
ISharedImages 介面提供語意化 KeyImageRegistry 管理,提供公開 dispose()plugin.xml 暴露 ISharedImagesstop()) 時呼叫 dispose() 釋放資源使用方:PlatformUI.getWorkbench().getSharedImages().getImage(key),絕不參與釋放,遵循「誰建立,誰負責」。
dispose() 釋放(適合局部資源);或 Display Hook(長期資源)。JFaceResources.getImageRegistry():勿自行釋放,讓 Registry 管理(適合全域高頻共享資源)。ISharedImages:提供方負責釋放,參考 Workbench 實作。ImageDescriptor.createResource() 替代 new Image() 減少頻繁建立銷毀開銷;長期資源用 Registry。註:文章趕工產出,若有錯誤歡迎指正。