JER瘦身方法

本回主在描述精简一个Java应用的实际步骤,选取了ApoDefence这个小型的Java即时战略游戏作为用例。  该作者信息可以在http://home.arcor.de/newbielein/获得,这是一个德文网页,作者也提供了其它一些小型Java游戏的展示,比如:

  图片看不清楚?请点击这里查看原图(大图)。
  有兴趣的可以下载作为参考。
  ApoDefence   是一个Java制作的2D即时战略游戏,游戏主题是本国要塞的加固及本国城堡的防护,虽然作者所提供的功能对比商业化游戏远不够完全,但是即时战略游戏的基本要素,如小地图,点选对象,建筑的搭建、修理、破坏、计时,建筑升级,多兵种协作,敌我互动及地图编辑都已经在由此游戏中展现出来,应该这个程序说对于开发Java即时类游戏而言是一个不错的参考范例。
  ApoDefence主要文件有两部分,一是ApoDefence.jar本身,一是levels文件夹下的脚本地图,两者累计大小为734KB。
  应该说,如果它永远只有这么大,将他放到同人游戏站点上,下载量是绝不会输给其他语言开发的同类游戏的。
  游戏界面1:

游戏界面2:

  图片看不清楚?请点击这里查看原图(大图)。
  但遗憾的是,这个游戏是Java开发的,如果没有动辄数十兆的虚拟机支持,它是很难跑得起来的。
  试问你看到这么一个小游戏,记住,也仅仅是个小游戏,它似乎很好玩,却需要下载十几甚至几十兆的安装程序,你还会有兴趣去尝试吗?我相信大多数人的答案会是否定的,所以,我开始试着精简它。
  第一步:整理你的RT.JAR文件
  我们都知道,JRE的完整版体积是很大的,在其文件夹内充斥着无数的dll,jar及properties配置文件,但要说到其中那个文件最大,最占用空间,则首推rt.jar文件莫属。
  以JRE1.6中的rt.jar为例,仅它一个jar,便占用了将近46MB的硬盘空间,这无疑是种垄断,严重挤压了其它jar同类的生存空间~~~

  图片看不清楚?请点击这里查看原图(大图)。
  前两天我在Blog发过一篇文章,讲的就是这件事(精简jre体积),我提供了一个名为GreenJVMMake.jar的6KB小程序,它能够准确的记录每次启动Java程序所调用类与rt.jar中类的对应关系,并且在程序执行结束后生成一个仅携带调用类的rt.jar。
那么,我们要开始精简rt.jar了。
  首先,我们制作一个简单的bat批处理文件,内容如下:java   -jar GreenJVMMake.jar -t da -i ./ApoDefence.jar -o   ./
  为的是调用ApoDefence.jar图形程序,并将精简后的rt.jar在本地输出。
  我们可以看到,随着我们的操作,所有被调用过的类都被记录下来了,事实上由于组件大多数都会重用的关系,只要运行几个典型界面,就可以把整个程序使用到的class全部打包进来了,而不必一直玩到通关。

  图片看不清楚?请点击这里查看原图(大图)。
  现在我们来看看成果吧,一个原始大小将近46MB的“怪物”,现在在这个Swing游戏环境中,还剩下多少呢?20MB吗?10MB吗?不,比那要小得多,答案是仅有不足3.2MB了。

  图片看不清楚?请点击这里查看原图(大图)。
  这么小的库能够跑得起来这个Swing游戏吗?我们可以先在本机试试看。

 图片看不清楚?请点击这里查看原图(大图)。
  就结果来看,答案似乎是肯定的,我们精简成功了。但事实果真如此吗?未必。我们将程序发布到一台从未配置过Java环境的机器看个究竟。
  PS:本示例使用自制的GreenJVM作为虚拟机引导程序,详细见:http://blog.csdn.net/cping1982/archive/2008/08/21/2806598.aspx

  图片看不清楚?请点击这里查看原图(大图)。
  我们可以清楚地看到,虽然开始启动后已经载入了作者的丑陋嘴脸~|||……但GreenJVM却同时提示给我们一个Exception,显示找不到sun.font.StandardGlyphVector类,但我们在本地调试得好好的啊,究竟为什么呢?
  其实原因很简单,就在于我们的JRE是针对A系统环境而配置的,而并非B系统。
  大家都知道,无论是JRE或者我们开发Java程序时,为了最大限度发挥Java跨平台特性,都会对不同的系统采取不同操作,从而提高本地环境下Java执行效率。
  但是,当我们利用GreenJVMMake.jar进行rt.jar精简后,则只会获得本机环境下需要的类,却并非所有环境下都通用的类,异常产生的原因也在于此。那么我们应该怎么办呢?
  其实很好解决,第一、由于我们使用的是自带JRE,决不会存在版本冲突问题,无论调用方式怎样,最终的结果,绝对是特定版本产生的,也就是不可能存在针对版本优化而缺少类库的顾虑。第二、事实上能够针对不同环境的配置变更并不多,要说最常见的,那就是语言与字体。比较典型的就是我们刚才所见的font包异常,这类异常大多是由现有   Java字库对本地环境的不支持所产生的,那么针对在不同环境使用的程序,我们在精简时可以刻意的保留下所有font及所有如zh_cn、en等针对不同语言所定制的对应类(英文程序则没必要保留所有语言支持),这样虽然程序体积比之前略大些,却可以从根本上解决问题。
 现在,我替精简的rt.jar重新拷贝所有的font支持类(这部分我在GreenJVMMake.jar中添加了完整保留指定库的功能,由于改动太少暂时没有发布,自己ctrl+c和ctrl+v也不麻烦)。
  替换后的精简rt.jar大小如图:

  第二步:删减不必要的exe,dll及其它一切配置文件。
  这步说简单也简单,说麻烦便麻烦,简单的是举凡rt.jar中不调用的,除了jvm.dll(它调用一切),我们尽管删掉即可;麻烦的是目前为止还没有一个工具提供了这样的功能(考虑写一个),现在我们只能依据经验来判定这些文件是否会被rt.jar调用了,这部分我准备写再下回时说,先卖个关子。但有一点最基础的大家可以注意一下,那就是通常这些文件名会有和类名互相对应的部分。
  第三步:压缩精简后的JRE
  这时有两类方式可供选择。
  一、使用Install4J等工具制作精简JRE。
  我这里建议使用Install4J,这是目前为止对Java软件支持最全面的安装包制作工具,我个人目前所使用的是4.1.4版。

  我们以Install4J为例,直接通过选择[Create   a JRE Bundle] ,创建一个自定义的JRE包。

选择精简后的JRE所在路径,版本,及扩充名(Install4J识别用)。

  图片看不清楚?请点击这里查看原图(大图)。
  此时的Install4J对JRE来说,会有两种不同的压缩方式,一是针对JRE1.5以下版本的直接压缩,二是针对JRE1.5以上版本的pack200压缩jar,再二次压缩成JRE包。
  当我们使用JRE1.5以下版本方式压缩JRE包时,通常不会有任何问题,因为这时Install4J只是单纯的压缩一个gz格式文件。

  如上图所示,针对JRE1.5以下版本进行JRE压缩成功,最后生成的文件将位于Install4J目录下jres文件夹中,本例生成的JRE文件大小约5MB。
  而当我们以JRE1.5以上版本压缩JRE时,问题就来了,因为此时Install4J将调用pack200进行jar压缩。
  对一个完整的JRE打包来说,拥有pack200相关功能当然不是问题,可在使用精简的JRE时,却变得不一定了。

  图片看不清楚?请点击这里查看原图(大图)。
 瞧,刚才还能正常运转的Install4J,此刻却罢工了。理由很简单,我们的精简包中没有打入pack的类支持,是的,运行Install4J的JRE1.5以上版本打包不光需要Pack200.exe文件,同时需要rt.jar中的类库支持。
  没办法,我们制作一个完整调用pack类库的小程序,而后以此生成仅有依赖库的rt.jar,合并打包进自定义的rt.jar中,再次运行程序。OK,通过了。

  图片看不清楚?请点击这里查看原图(大图)。
  经过Install4J打包的jre1.5以上版本jre能够压缩得相当厉害,我们原本将近10MB的文件经其压缩整理后只有2MB多一点。
  但是Install4J毕竟是一个收费软件,况且现在的环境下流行绿色软件,也就是所谓的免安装无插件不修改注册表或其它系统文件的程序……毕竟我们有时也希望能将Java程序作为一个绿色软件进行发布。
  二、使用7-Zip等工具制作精简JRE。
  现在,我们以7z格式制作一个jre压缩包。
  首先,我们学习Install4J的方法,先将所有jar进行pack200打包,由于pack200是针对jar定制的压缩程序,所以能最大限度的减小jre中文件体积。
  由于本示例使用GreenJVM作为jar引导程序,所以包名后缀命名为[pack200],当GreenJVM初始化时将自动调用unpack200.exe解压文件所在目录及子目录下所有[pack200]后缀的文件。
  其实,我们使用压缩工具对JRE文件夹进行压缩,本例中我们使用7-zip进行压缩。
7-zip是一个源码开放的基于lzma方式的免费软件,单就文件压缩率而言,较rar格式更高,下载地址:http://www.7-zip.org/

  图片看不清楚?请点击这里查看原图(大图)。
  此后我将将得到一个jre,现在这个总体积约5MB的jre包,就是最终可以执行此Swing游戏的环境了(相较于Install4J压缩后的JRE包而言,这个JRE压缩文件还是大了,利用某些机制还可以压得更小,下回介绍)。

  无论我们采取那种机制,这个JRE最终发布版都会被维持在5MB以内。
  第四步,打包发布。
  1、直接进行压缩打包
  这点其实没什么可说的,如果你使用GreenJVM制作的绿色Java程序,那么你只需把用到的程序连带精简后的JRE都丢给用户就好。双击exe文件运行我相信大部分使用过电脑的人还是明白的……

  另外有一点需要补充说明,就是同种压缩方式二次压缩将很难使文件再度缩减,比如本例我们以7z格式打包jre,那么如果针对整个应用制作再次压缩文件则选用rar格式更合适。
2、使用Install4J进行打包发布
  比较大多数Java开发的程序而言,上面这个绿色发布版的体积可说是相当小了,已经能满足绝大多数Java游戏的需要(除了没声音,因为原游戏没调用),但是对比本地环境开发的程序来说依旧太大,用户并不一定便会接受,这时我们为了让他更小一些,单纯的压缩已经不能派上用场,非借助安装工具的多重压缩精简不可了。
  依旧使用Install4J,它的视图安装方式极为简单,正宗的傻瓜式“下一步”就可以搞定,时间关系这里不作详细说明,直接跳到设定媒体文件模式。
  由于此示例基于windows系统开发,所以仅设定windows平台支持。

  图片看不清楚?请点击这里查看原图(大图)。
  经过一系列下一步后,选择我们制作的JRE,静态绑定。

  最后,我们选择构建一个Windows平台下的安装程序。

  图片看不清楚?请点击这里查看原图(大图)。
看吧,这就是最终的结果。

  一个仅仅1.25MB的Swing即时战略游戏,试问如果我们不说这是Java开发的程序,又有几个人能想得到连虚拟机带执行程序打包后也仅仅1.25MB呢?而且事实上,它还可以变得更小,乃至精简到1MB之内。(留待下回说明)
  现在,我们将它安装到没有JRE的计算机上去看看结果吧。

  图片看不清楚?请点击这里查看原图(大图)。

  图片看不清楚?请点击这里查看原图(大图)。
  炮弹照打,狮子照跑~~~完全OK。
  那么下回,我们将进一步寻找究竟有哪些因素限制了JRE的体积,并进一步解放JRE。
  (由于Install4J打包有一些细节准备下回分解,所以安装程序暂时不发,透露一点,压缩的JRE要想正常使用Install4J发布,还需补充一些文件,可参见Install4J正常安装时释放在temp中的某些东西~)

你可能感兴趣的:(JER瘦身方法)