J2ME内存优化

 

 

内存优化可以归结为以下几种方法。代码优化,图片优化,第三方工具优化等…


一.代码优化
  内存会溢出肯定和代码逃不了关系,99.99%学java的人都知道垃圾回收器是java的一大优点并据此来嘲笑C++。显然这个特性为代码编写者省了不少事,但这个特性却带来了不少隐患。举个例子在游戏当中经常有不同场景的切换,如从游戏逻辑退到主菜单逻辑,对游戏逻辑对象的态度很多人会选择忘记等待垃圾回收器来收尸^_^。乍看之下似乎并无不妥垃圾回收器会来善后。实际上垃圾回收器并非实时的,它不像C++的Delete语句马上释放不用的内存。当从游戏逻辑切换到主菜单逻辑这时两个对象同时存在很可能这时内存就不够用了。读到这里很多人会发现实际上垃圾回收器在j2me上并不怎么好用,从一个角度上来讲在j2me上所有垃圾必须由手工释放,除简单类型以外所有对象都必须显式地置空例如 imgs=null; 实际上java提供了一个不错的工具用来查找内存溢出,java.lang.Runtime.freeMemory() 。它可以返回当前的剩余内存数,将它适当的安放在代码中可以有效的监测内存使用状况。

很大一部份的j2me程序员之前都是从事pc软件开发工作,充裕的内存掩盖了许多写代码的不良习惯。如下所示:

//a 不为空
a=new Logic();

  很多人可能对此有异议,他们会认为新的对象会把旧的对象冲掉并且释放内存。这里面包含两个问题:1. 该段代码是先创建对象然后再进行赋值操作的,也就是说在这期间有两个对象同时存在这就很可能会产生溢出。2. 这样做也会妨碍垃圾回收器的工作。

较好的写法如下:

a=null;
a=new Logic();

虽然麻烦了点但在j2me中还是必要的。接着看下例。

drawString("游戏时间:" + time ,50,50,Graphics.LEFT|Graphics.TOP);

"游戏时间:" + time 很完美在paint()方法当中每次都被刷一遍显示在屏幕上。危机往往隐藏在美丽的外表,该语句会引起新的内存重新分配来存储 "游戏时间:" + time 而显示完以后又必须由垃圾回收器释放,用了双倍时间,并且容易发生内存溢出。依此类推在重复执行的方法里应尽量避免重复定义对象。与paint()方法类似在循环里也有类似的情况存在。

  把所有对象的初始化放在构造函数里想必是再正当不过了,大多数人通常的做法是把当前逻辑所要用到的资源通通初始化完毕。

  很大一部份的内存溢出都是发生在构造函数中。内存使用的高峰期都是在构造函数中所以避开这个高峰能有效的防止溢出。建议最好的办法是第一次使用时初始化。如下所示:

if (img==null){
//初始化
}

现在做游戏很多时候都需要地图数组,声音数组,还有一些其它资源这些资源很多可以放在代码中也有的可以放在文件当中。

  强烈建议将这些资源放在文件中需要时在load进来。这些资源文件如果放在代码中则会占用不小的代码段空间,而代码一般是程序一运行就装载到内存当中。

  除上面列举的方法外还有一些大家所熟知的顺便一提, 比如关闭没用的rms ,关闭没用的网络连接,关闭没用的流。正确地停止线程。良好的程序架构减少代码偶合性也是一个不错的方法,无论在代码调式,内存释放都可以做到非常清析。


二. 图片优化
  J2ME的内存杀手无疑非图片莫属,一张3k的图片可以占用20多k的内存不信大家把load前后的内存剩余打印出来对比看看。所以防止内存溢出最直接的办法就是从图片入手。

1.图片压缩: 多数人马上会想到这个办法。不错这个办法是最有效的。在photoshop里图片制作完成后不要选择 "存储为",而是选择 "存储为 web 所用格式" 可以根据里面的选项进行压缩,特别是颜色这一项越小越好不过相应的图像会有所失真。不要认为这样就完了。
实际上该图片还可以再次压缩,在网上有许多类似的工具。推荐一款可以压缩png格式的软件 xat.com Image Optimizer 效果不错。经常都有 70% 的压缩率且图像不会失真。

假如你有多张规格一样的图片,那么建议你把它做成一张长条图片。有两个原因:

1、这样节省存储空间和内存空间。大家可做个试验将10张图片的内容放在一张当中对比看看文件大小有没有变化。
2、10张图片需要10个image 对象需要进行10次io操作浪费时间不说还浪费内存。当笔者发现这个好处时兴奋地把所有图片都存成一张,吱地一声内存又溢出了...原因想必大家也知道!!图片太大了不要把不同界面的图片整合在一起否则经常会得不偿失。

  作图时还有一些细节需要注意,颜色数量,分辩率,图像模式(最好是索引颜色),画布大小都会影响到图片大小。


三.工具优化
  谁都知道混淆器是用来保护代码的以加大反编译的难度(个人认为这是在嘲笑程序员的智商)。实际上用它来优化程序也是不错的选择,至少有两点好处:1、 压缩程序大小。一个60k的程序经常可以压掉10k左右。10k的空间对于写低端手机的程序员简直是雪中送碳,多少超过64k限制的游戏都受过它的恩惠;2、节省内存空间。用脚去想也想得出来代码少了内存里的代码段自然就短了。


  

 

J2ME开发优化

1、猜测Vector的长度

2、使用局部变量

3、避免字符串比较

4、将变量和方法声明为final和static,以加快访问

5、使用惰性实例化

6、处理outofmemoryError错误

7、使用数组而不是对象

8、倒计至零的迭代循环,因为和零比较将会较快

9、把引用设置为NULL

10、最大限度的减少方法调用的次数

11、使用移位运算取代乘以二的运算

12、避免类型转换

13、只要有可能、尽量使用Int

14、避免使用异常

相对于J2SE和J2EE开发,很多反常规的方法倒是更加适合J2ME开发

部分内容摘自《More Java Pitfalls 中文版》

 

J2me程序由于其特殊的运行环境限制,所以优化就显得比较重要,以下是我在学习j2me编程所收集的一些技巧和自己的心得。

  本文主要是说明j2me平台上的特殊优化方法,与j2se重复的一些技巧就不再赘述了。

  1.显示图象时确定好你的fps,最好先做几次小实验,这样能让你在显示效果和运行速度上有比较好的平衡。

  2.GamaCanvas.getGraphics()每次都会产生一个全新的对象,但是对这些对象的操作都是对同一个graphics,所以还是只取一次供后面使用。

  3.让多个对象使用同样的监听器,比如让主MIDlet类实现CommandListener和ItemStateListener接口。

  4.考虑使用手机开发商提供的一些sdk,没人会比他们更了解他们手机,所以有些时候能显著提高速度,特别是图片,视频使用方面。

  5.使用监视工具分析MIDlet的瓶颈,wtk和各个公司提供的开发包里都会有,可以找到程序的弱点。如果是在手机上,用timer测试你认为有可以的地方。

  6.使用System.gc(),在无线程阻塞的情况下可以有效的缓解内存压力,但是有些公司不是太推荐使用(如nokia).sun的说法也是越低端的机子执行的越慢,总之,慎用吧。

  7.用固定的数组代替使用Vector。

  8.图片的优化。考虑使用设备的规格,可能高分辨率的图片不一定显示的出来。

  9.不用的对象赋值为null,为更快的回收

  10.用混淆器处理你的类文件,防止反编译,还有一个好的副作用就是它减小>

=============================================================

通过Profiler对各种函数及程序的测试,我总结如下结论:

  ·仅当你需要的时候才去优化代码!

  ·仅优化那些最耗时的代码!

  ·使用Profiler去查找哪里需要优化!

 

  ·记住Profiler不代表真机上的优化结果,使用System Timer来在真机上做最后的测试!

  ·在做低级优化之前,总是要先思考算法是否是最优!

  ·绘图是很占用时间的,所以尽可能的减少Graphics函数的调用!

  ·尽可能的使用SetClip()来减少绘图区域,相对于SetClip(),drawImage()所花的时间会更可观!

  ·尽可能的将变量定义在循环以外!

  ·尽最大可能的进行对需要的数据进行预先计算并将结果保存在缓冲里!

  ·String类很容易产生垃圾内存,尽可能的使用StringBuffer代替String或用final static来定义之!

  ·假设是不被接受的,一切要以真机为据!

  ·尽量使用static final修饰函数,而避免synchronized修饰符!

  ·对于频繁调用的函数要使用尽可能少的参数!

  ·尽可能的不使用函数调用!

  ·尽可能的使用<<和>>来代替*和/!

  ·使用位操作来代替%运算!

  ·与0比较比与其他数值比较快!

  ·数组存取比C语言慢,尽可能不在循环中存取数组!

  ·局部变量比其他类型的变量运算要快!

  ·在switch()中尽量使用连续的小数值判断!

  ·尽量使用乘法而不使用除法!

  

  ·尽量使用已有算法!

=========================================================

 

一般就着手于代码的优化,像电力就没办法了,

J2ME的编程,由于平台的原因,不能像电脑的编程一样,采用很良好的设计,要知道增加代码的数量就可能造成一定

的影响,所以不能完全追求设计而编写,比如setter,getter方法如果不涉及自定义操作的话,可以将变量设成public的,这样一来就可以避免方法调用所产生的开销

还有,图片使用方面,能使用拼图就尽量使用拼图,不要用一张过大的图片,图片的质量能压缩尽量压缩,颜色能少用

就尽量少用.

声音也是一样,尽量用短小的声音,不要使用太大的声音.

尽量少用对象,不用的对象及时NULL掉,图片,声音资源也一样,用的时候加载,不用的时候NULL掉,可以在适当的时候,用Runtime.getRuntime().gc()清下内存,画图的时候用rapint(0,0,屏幕宽,屏幕高),防止超出屏幕外的部分也绘制,尽量用MIDP1.0的方法,效率比2.0的要高,import的时候,只import要用的类,不用的就不导入,记得打包的时候要混淆下,用单线程,少用VECTOR,尽量用定长的对象数组。

[原创]J2me的优化之路

[watermark]J2me的优化之路 很多介绍优化的文章里都提到要尽量减少class数量,但是,这真的是有必要的吗?不错,每增加一个空的class大概要多使用100多字节内存,打包后的体积也会有少许增加。但是这样做的好处是更清晰的代码结构,明确的变量使用。因为众所周知,java里没有c/c++里的struct,union,等数据结构,j2me里只有使用class来代替。 代码的优化,不是用搞乱程序结构来实现的。诚然,那样做的确可以或多或少的减小jar体积,但是代价呢?不清晰的流程,混乱的变量使用……,甚至导致无法二次修改、移植。这样的代码,我宁可不要。 真正的优化,是建立在清晰的结构,明确的流程之上的。合理的使用变量,优化数据结构,优化众多不变的数据成为常量,删除无用的代码诸如:System.out.println等调试时期使用的代码。 对于运行速度的优化,几乎是所有编程语言都通用的。请看如下伪代码: Object m_sprite[]=new Object[5]; int i; for(i=0;i<10;i++) m_sprite[0]=xxxxxx; 在for的10次循环中,m_sprite[0]每次都要访问2次地址。为什么?不要问我,有c/c++开发经验的人都应该知道。 如何优化这个部分?请参考下面的代码: Object m_sprite[]=new Object[5]; Object tmp; int i; tmp=m_sprite[0]; for(i=0;i<10;i++) tmp=xxxxxx; 仅仅增加一个变量,就将运行速度提高的近乎一倍。诸如类似的优化方案还有很多,多是千篇一律,大家可以从网上搜索这里就不重复了。请记住一点,所有的优化方法几乎都是通用的,适用于c/c++的,一样适用于j2me。 另外,建议大家使用TimerTask,而不是Thread,除了TimerTask更精确一些外,还因为如果使用Thread方法,就必须依靠wait或sleep来控制时间,但是这两个方法根本无法保证精确性,几乎每个型号的手机都不一样。 排除手机性能的自身问题外,有一部分速度的影响是在屏幕刷新上。paint是一个异步方法,并不是保证立刻刷新,即使调用serviceRepaints也一样。超过屏幕所能接受的刷新频率将导致图象的混乱和更长的延迟时间。 合理的安排资源文件也很重要,jbuilder ,eclipse都具有指定打包后文件所在路径的功能。另外,缩短资源文件名,将多个资源文件合并为一个文件等方法也可以有效的减小jar尺寸,原理在于zip的压缩算法。 最后的优化,建立在一个名叫ProGuard的混淆工具上。目前最新版本是3.4,这个工具的特点是可以删除,合并没用到的class,方法,变量,取消package限制将所有class放到根路径,从而大大缩小jar的尺寸。另外需要注意的一点是,class的数量,请尽量保持在26个之内。否则,混淆后的变量名不但不会缩短,反而会更长。 j2me的优化之路还很长,我仅仅是摸索到了一点点,更多的东西还需要更长的时间去发掘。以上观点,如有不对之处,欢迎探讨。[/watermark]J2me优化

总结如下:

1)尽量要重复利用对象;

2)不用的对象要赋为null;

3)数组代替vector;

4)尽可能少用class,如果能用过程的方法就用,不能的话再考虑对象;

5)方法的速度比较:同步方法<接口方法<实例方法<final方法<静态方法

6)尽量使用final static方法,当然这个方法的含量要少了,这样可以提高速度。

7)数据成员设成public吧,省得要调用函数去修改!

8)在进行循环的时候,我们要使用++i,不要使用i++,我曾经试过,在大型的循环过程中,时间要快1倍。

9)在进行大的数组初始化的时候,最好使用读取的方法,这样可以省去很多内存。比如256*64的数组,如果我们使用interface的话,要用掉24616的memory,而使用读取的方法要使用13408的内存。

 

    代码优化的技术大致分为两个主要方面:高级优化,从使用的整体算法和结构出发进行的优化;低级优化,集中于孤立的代码片断(通常为方法中的代码)的优化。下面分别讨论两方面的优化:

一,高级优化

1, 感觉到就是真实

对于电影来说,我们通过摄像头看到的都是完美的,而在拍摄现场我们看到的却是木头,泡沫和胶带。所以对于电影来说,感觉到就是真实。

游戏也一样,只需要处理游戏需要的东西。在游戏开发的各个方面这都是实用的。把精力集中在使游戏有趣和完美运行的问题上,始终只做需要做的而丢弃其他的部分。

2, 不要创建对象

减少对象创建的总数量和频率,结果能够大大地提高游戏的性能。还必须小心在不经意的情况下产生String对象。

例如:graphics.drawString( 0,0,”Score:” +score );

这一句代码会在每次被调用的时候产生一个新的String对象,在这里就是每一桢画面显示时都会产生新的String对象。因此最好是只是在分数改变的时候才构造这个String。

3, 绘制屏幕

通常,在对游戏完成大量的优化工作以后,收获的将是一个大量时间耗费在屏幕绘图上的游戏。这是因为一个游戏的主要时耗大都集中在绘制图像的工作上(或其他的一些基本的绘图调用)。因此,如果一开始就可以避免绘制工作,那将是对游戏的很好的优化。

还有就是要减少屏幕绘制,循环检测屏幕图像是否在某个部分发生了改变,如果没有,就不要对那部分的屏幕进行更新。另一个方法就是增加绘制图像的尺寸来减少单独的绘制调用的次数。

4, 算法

最好的,也是使用最多的高级优化是对游戏的算法方面。

二,低级优化

1, 提前绘制复杂图像

我们已经知道,使用LCDUI绘制图像是很慢的,因此最好是能够避免这种绘制。其中的一个方法就是用一个预生成图像来减少复杂图像的绘制。进一步来讲,举例:将所有的游戏状态信息整合到一个面板中(得分,生命数,能量值等),然后对这些信息进行一次性同时更新。

2, 保持类和内存之间的平衡

产生新的类会增加JAR包文件的大小,因此应该尽量避免。有的时候增加了额外类的开销可能节省了额外的内存开销,这也是值得的。

3, 复杂值的预计算

节省运算的一个好方法就是对数值进行预运算,从而无需再调用大开销的计算方法。一个很好的例子就是:主窗口画布的高度和宽度就是很好的 缓存对象。例如:可以调用getHeight方法和getWidth方法一次,然后将它们的结果缓存起来,而不是在每一次绘图中都调用这两种方法。

4, 使用数组

在任何时候,只要可能,都应该使用数组而不是Vector,因为数组的运行速度更快。通常面临的唯一问题是,如果最初分配的数组空间不够大,将需要对数组的大小进行扩充。这可以做到,但它需要对整个数组进行重建。例如:

    Public final static int[ ] eXPandArray(int [] oldArray, int expandBy)

           {

                  int [ ] newArray = new int [oldArray.length + expandBy];

                  System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);

     Return newArray;

}

      任何时候,都应该尽量使用一维数组。访问二维数组变量的速度只有访问一维数组变量的一半。当然,仍然可以访问二维数组的对象,只是需要加入一点点计算。例如,与其使用这条语句:

              world[y][x] = 0;

       不如下面这条语句运行的快:

        world[y*tilesWide + x] = 0;

这条语句通过行列的位置将数值转换成一维值,实现了对数组同一元素的访问。

5, 不要使用数组

呵呵,尽管数组的访问比Vector快,但仍然比直接访问变量要慢,因此如果可能就应该删除对数组的访问,或者为一些常用方法中的数组寻求其他能提高性能的办法。

6, 使用快速方法

并不是所有Java调用的方法在性能上都是相同的,方法声明方式的不同对性能会会产生很多的影响。可以使用的最快的方法类型是静态方法,因此应该尽可能多地将代码置于静态调用方法中。运行速度仅次于静态方法的是声明为final的方法。运行最慢的两种方法是在接口中定义的方法和用关键字synchronized声明的方法,必须尽可能地避免使用这些类型的方法。

7, 其他优化

1)异常处理非常缓慢,不要为一半的游戏逻辑使用异常,只用它们来报告真正的错误状态。

2)使用switch表达式比使用if条件语句块的速度要快。

3)尽可能避免使用String对象进行运算,使用StringBuffer。

4)内嵌类的运行很慢,尽可能避免使用。

5)在完成一个引用的使用后将它设为null。

6)不要浪费时间来将一个对象初始化为null或0,java虚拟机会替我们完成这样的初始化

7)多思考新方法,这会使我们的大脑运转的更快。

8)如果可能,尽量使用static,它们运行都很快。它同时适用于方法和域,这条规则就是,如果它可以是静态的,那么就把它声明为静态的。

9)避免类型转换。

">必�#�0Wn��Q �P a™)

 

纳入小的修正并更新 Visual Studio 2005 中所有面向数据的对话框,以显示 SQL Server Compact Edition 产品名称。对于 Windows Vista 用户,为使 Visual Studio 2005 能够在 Windows Vista 上运行,必须安装此服务包更新。

.NET Compact Framework 2.0 Service Pack 1

推荐

如果没有此服务包,则每次部署应用程序时,Visual Studio 2005 都会将 .NET Compact Framework 2.0 部署到 Windows Mobile 6 设备。还添加了对命令行调试的支持,并提供了新的实用程序来审查 .NET Compact Framework 2.0 运行时所提供的性能数据。

SQL Server 2005 Compact Edition Tools for Visual Studio 2005

可选

提供开发使用 SQL Server 2005 Compact Edition 的 Windows Mobile 6 应用程序所需的工具和程序集。安装此工具前,需要先安装 Visual Studio 2005 SP1。


 

充分利用现有技能

如果您曾经构建过 Windows 桌面计算机应用程序或服务器应用程序,即使您以前从未构建过 Windows Mobile 应用程序,您也已经拥有了着手开发 Windows Mobile 6 应用程序所必备的技能。使得着手构建 Windows Mobile 6 应用程序变得如此容易的一个因素是 Visual Studio 2005。在 Visual Studio 2005 中创建 Windows Mobile 6 应用程序与构建桌面 Windows 窗体应用程序非常类似。在开发 Windows Mobile 6 应用程序时,Visual Studio 2005 的行为和所提供的功能与在开发桌面应用程序时是相同的。

托管应用程序开发

如果您是托管应用程序开发人员,您会发现 .NET Compact Framework 2.0 提供了与创建桌面应用程序时使用的类相同的类,这些类是完整 .NET Framework 类的一个丰富子集。.NET Compact Framework 2.0 还提供了许多与构建桌面应用程序时所使用的 Windows 窗体控件相同的控件。这些控件所提供的体验是您开发桌面应用程序时所熟悉的相同体验。

本机应用程序开发

Windows Mobile 6 平台的核心 API 是 Win32,尽管它是一个子集,但与桌面计算机上的 Win32 是相同的。与此类似,活动模板库 (ATL) 和 Microsoft 基础类 (MFC) 都可用,二者都是其桌面计算机对应项的丰富子集。您还会发现,为桌面应用程序编写的许多 C\C++ 代码与 Windows Mobile 6 兼容。兼容性如此高是因为 Visual Studio 2005 为桌面应用程序和 Windows Mobile 设备应用程序使用了相同的前端 C\C++ 编译器。

数据库应用程序开发

您还会发现,可以将开发面向 SQL Server 2000 和 SQL Server 2005 的数据库应用程序的技能和经验轻松地转换为开发 SQL Server 2005 Compact Edition 应用程序的技能和经验。SQL Server Compact Edition SQL 语法与 SQL Server 2005 完全兼容。类似地,面向 SQL Server Compact Edition 的 ADO.NET 和 OLEDB 编程接口也与对应的面向 SQL Server 2005 的编程接口一致。甚至创建和管理 SQL Server Compact Edition 数据库的方式也让人觉得熟悉,因为创建和管理 SQL Server Compact Edition 数据库时所使用的 SQL Server Management Studio 工具与创建和管理 SQL Server 2005 数据库时所使用的 SQL Server Management Studio 工具是相同的。


 

指导

Microsoft 提供了许多资源,以为您提供在 Windows Mobile 6 平台上构建优质应用程序所需的指导。除产品文档外,还可以在许多位置找到有关 Windows Mobile 应用程序开发以及在 Windows Mobile 平台上成功开发的最佳实践的信息。

MSDN® 上的 Windows Mobile 页面多半是最好的起步位置。它提供了有关 Windows Mobile 开发的最新信息。MSDN 上的 Windows Mobile 页面还包括指向其他资源(例如白皮书、有帮助的博客和网络广播)的指针。

Microsoft 模式和实施方案小组在移动客户端软件工厂中为 Windows Mobile 开发人员提供了出色的指导。移动客户端软件工厂是一个综合性的面向 Windows Mobile 开发人员的最佳实践、示例、源代码和工具集。所论述的主题包括处理设备外观造型差异、离线数据管理、数据同步和管理大规模应用程序。

Windows Mobile是 MSDN Channel 9 站点上的一个社区支持的项目。Windows Mobile Wiki 是从其他开发人员的知识和经验中获益的极佳途径。您会发现该 Wiki 致力于解决许多最常见的问题。

Windows Mobile 新闻组是一个社区支持的资源,可在其中提出有关 Windows Mobile 开发的问题,并让业界同行(包括许多 Windows Mobile Microsoft Valued Professionals (MVP))回答这些问题。

Windows Mobile 调整您的应用程序页面提供了与开发能够在全系列 Windows Mobile 设备上工作的应用程序有关的问题的最新信息。您会发现专门针对这些问题的新闻、公告、网络广播的指针和白皮书的指针。

 

结束语

Windows Mobile 6 提供了开发人员开发和部署功能强大的移动行业应用程序所需的工具和功能。在所有 Windows Mobile 6 设备的 ROM 中加入 .NET Compact Framework 2.0 SP1 和 SQL Server 2005 Compact Edition 提供了一个功能强大的应用程序平台,它也使应用程序部署得到了简化。Device Emulator 2.0 和 Windows Mobile 6 SDK 附带的其他工具通过提供能够在多种 Windows Mobile 设备特征和外观造型上更轻松地测试应用程序的能力,简化了能够在许多不同设备上运行的应用程序的开发过程。Windows Mobile 6 SDK 中包括的 100 多个示例、模式和实施方案移动客户端软件工厂以及移动行业 Solution Accelerator 工具包的组合都是为了提供立即启动行业应用程序开发所需的指导。

无论您是经验丰富的 Windows Mobile 开发人员还是正准备开发第一个 Windows Mobile 项目,您都会发现 Windows Mobile 6 是可用于行业应用程序开发的最佳 Windows Mobile 平台。

 

posted @ 2014-08-04 14:00  尽人事以听天命  阅读(126)  评论(0编辑  收藏  举报