昨天在阅读 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的进化已经非常明显了。

这个结论或者比较偏颇,考虑应用目前的实际情况,我还是决定不再做任何反向修改了,够用就好。