昨天在閱讀 PROGRAMMING MOBILE DEVICES AN INTRODUCTION FOR PRACTITIONERS 一書時,又再次提到j2me設計的一個重要拇指規則
Avoid small classes
我可以理解小類比萬能大類為何更增加內存消耗,一方面是增加了類型聲明區域,另外一方面是增加了交互引用的資源。
用wc的話說,一個對象引用也要2個字么。不過一直以來覺得這點消耗不算什么,相比起來小類那種更oo的做法帶來測試
和維護的好處,那才是我關心的。
但是看到書中寫的這個例子有點震驚。
considered by developers. For instance, the memory consumption of an application
that was implemented using two different structural alternatives, shrank to almost
half from 14 019 bytes to 7467 bytes when the number of classes was reduced
from 14 to 1 without altering the behavior of the application (Hartikainen et al.
2006).
節約了將近一半的內存空間,每個小類會多消耗系統的0.5k左右的內存。這超過了我的想象了
書中對這種情況做了進一步解釋,大意是小類會大幅度增加引用,也就是metadata的數據。
Because linking is performed using the actual names, the profile of memory consumption
also includes a lots of metadata
此處的linking的意思就是指java的類和類的引用都是動態鏈接的。
并對cldc的類庫做了分析。
Average class file content in CLDC library classes
Metadata 45.4%
Strings 34.8%
Bytecodes 19.1%
Class field data 0.4%
Instance field data 0.4%
可以看到metadata是內存消耗最大的一塊。
不過這個分析倒是和書中另外一個拇指規則有背。該規則強調不要使用繼承結構,因為繼承結構子類會裝載父類的實例變量,
增加內存消耗。 而這里的0.4%表面這部分一般情況下無足掛齒。
這是書中給出的實例的數據
Format 14 Classes 1 Class
No compression 14019 7467
No compression, obfuscated 13093 6929
JAR 10111 3552
JAR obfuscated 10048 3540
Pack 4563 3849
Pack.gz 2568 2235
這到讓我產生了好奇心,想花點時間測試一下, 小類是否真的如此消耗系統資源。
我選擇對當前的一個應用進行重構,做對比測試。
測試應用大約1w行代碼,35個form類, 20個左右和form對應的action處理類,都是所謂的只有2,3個方法的小類。 另外有30多個數據類和一些工具類。還有自己編寫的ui庫大約有30多個類。
這其中數據類和form類是無法合并成大類的。化了一晚上時間,將15個action類合并成一個大的action類。
原來的action類采用的是類似struts action那樣的繼承結構,基類中放置了一些通用方法和少量屬性。
類數量 |
編譯后體積 |
混淆后體積 |
wtk內存占用 |
n81內存占用
|
備注 |
145 |
1.42m |
184k |
336k |
512k 比例 2.782 |
|
修改以后的比較
類數量 編譯后體積 混淆后體積 wtk2.2內存占用 n81內存占用
130 1.43m 184k 345k
ft,有增無減么,有點崩潰的感覺,我剛開始理解體積增加是因為合并以后部分代碼增加了,后來仔細一想,出在匿名類上。
原來的action要和后臺做異步通訊,所以都設計成Thread,合并以后改用匿名類替代了,所以實際類未減少,還增加了一個,
,而匿名類,還有合并寫法增加的額外異常處理等代碼,都加大了體積。 但修改后的代碼至少減少了引用,看來所謂這個引用大量占用資源,也要實際情況實際分析。
為了核實這個問題,我刪除了所有方法中的代碼,這樣回避了匿名類增加的問題,實質上減少了35k左右的代碼。
類數量 編譯后體積 混淆后體積 wtk2.2內存占用 n81內存占用
130 1.41m 168k 246k 467k 比例2.779
在體積減少18k的情況下,內存消耗減少了45k。 根據我的經驗,n81上的初始內存占用情況基本是混淆后類體積的2.5-3倍左右。
所以無法推斷這部分內存消耗減少是否是因為類體積減少導致還是類數量減少導致的。但從比例看基本無關,可以初步認為在
n81上這條拇指規則失效了。而在wtk2.2里面則效果明顯。
wtk和n81虛擬機實現的差異按我的理解主要是kvm和 CLDC HI 的差別。這也說明不同虛擬機之間實現差異巨大,單純的說
避免小類使用其實不太靠譜。
另外看起來CLDC HI的實現和j2se 中的hotspot的實現一致,都是考慮硬件進步以空間換取時間的設計,所以初始內存消耗大幅度增加了。這越發引起了我的好奇,在cldc hi的vm上,難道避免使用小類這條拇指規則還有效么? 我嘗試尋找書中這個例子進行測試。
書中的例子來自某人的論文。而且大量討論j2me優化的文章都引用了此篇論文
Vesa-Matti Hartikainen, Pasi P. Liimatainen, Tommi Mikkonen, "On Mobile Java Memory Consumption," pdp, pp. 333-339, 14th Euromicro
International Conference on Parallel, Distributed, and Network-Based Processing (PDP'06), 2006.
可惜只找到要錢的,遲一點再玩。
初步結論: 避免使用小類這條規則在某些vm上是有效的, 但是對于高性能的CLDC HI實現,目前看來沒有太大區別。
這也說明j2me向j2se的進化已經非常明顯了。
這個結論或者比較偏頗,考慮應用目前的實際情況,我還是決定不再做任何反向修改了,夠用就好。