GCJ编译java程序的头痛问题

因为看到Dlee前面贴的关于GCJ的介绍,对GCJ发生了巨大的兴趣,花了1整天时间研究了一番,结果是先喜后悲,最后得到的结论是Java代码仍然不适合本地编译运行。

众所周知,Java代码运行速度慢,仔细分析,速度慢有几种体现形式:
1、JVM启动速度慢;JVM启动以后,加载基础类库,和其它各种类库的时间比较长
2、JVM运行Java字节码的速度不如本地代码快
3、Java的AWT和Swing图形库速度慢的夸张

在Java服务端应用中,这些问题都不是问题。因为App Server一启动就长期运行,只要类库加载到JVM以后,以后运行速度就快了;并且没有GUI,所以克服了1和3的缺点。App Server提供的各种缓冲功能完全抵消了2的问题。

在Java客户端,特别是GUI应用程序,速度慢是Java的主要缺陷。Eclipse Project的子项目SWT构建了一个新的图形库,通过JNI来调用操作系统本地图形库,极大的加速了GUI应用程序速度,同时减少了内存消耗。所以Eclipse IDE的运行速度和内存消耗已经相当接近本地代码的IDE工具。

GCJ提供了更进一步的改进,可以把Java代码直接编译为本地二进制机器码,成为了真正的本地应用程序,看起来确实非常cool。如果Java真能就此突破GUI速度瓶颈,恐怕就是天下无敌的编程语言了。

写了几个简单的应用程序包含Console程序,和SWT图形程序,使用GCJ编译,确实完全是binary code,运行速度也基本和本地代码一样。编译生成的exe程序运行的时候会调用一个swt的dll文件,此外调用的都是Windows的系统dll。已经彻底脱离了JRE运行环境,发布GUI软件的时候,只需要把swt的dll文件和生成的exe文件copy就OK了。

特别值得一提的是,exe文件经过strip之后,由6M多减到3M,和dll一起打包压缩,只有900多KB!真是小巧

如果Java程序都可以这样脱离JRE,编写好以后,编译成exe,打包发布,那么和VB,VC,Delphi这样RAD环境语言相比也丝毫不逊色了。

然而进一步试验,开始发现越来越多的问题。

首先,由于Java的开放性,各种功能的API类库多不胜数,GCJ能够做的也仅仅是把JDK的类库编译为C库文件,在link的时候,link到用户的exe程序上。而那些不是由Sun发布的官方类库,由于实在太多,就无能为力了。而且及时是JDK,据我观察和试验,其支持的水平大概相当于JDK1.2和JDK1.3之间,JDK1.4的很多类库都没有包括进去。所以如果你一旦用到GCJ没有支持的类库,整个程序就无法编译成exe了。

Java的威力在于无限的可扩展性,所以GCJ对类库的有限支持成了巨大的问题,使得相当多的程序无法编译为exe。当然,这个问题看起来也不是不能解决。我们可以这样做,把GCJ还没有支持到的类库,但是我们又需要使用的Java类库,自己动手编译成C的静态库,这样编译我们自己的程序的时候,link进去就OK了。

理论上可行,我做了几个简单的例子,也完全证明了可行性。其实简单的来说,这个步骤就是把Java的package转换为C的静态库。比如把
Java:JAXP -> C:libjaxp.a
可以这样来做
gcj -c jaxp.jar -o jaxp.o
ar libjaxp.a jaxp.o
gcj的语法和gcc基本上完全相同。

编译自己的应用程序:
gcj --main=... -o myapp.exe myapp.Java *.o --classpath=...jar -L. -llibjaxp

基本上是一个c编译和java编译的混合体,比如说需要指出java用的CLASSPATH,然后还要把jar包编译成的C库放在C的link路径上,同时用l参数指出库名

但是对于中文支持来说,好像目前还不行,Java源代码中有中文注释会报错,不过也有解决办法,就是先用Java编译成class,再用class编译成exe。这样虽然可以编译成功,不过执行的时候,中文输出就变成了乱码,看来中文支持的还不好。

但是在我尝试一个例子的时候,还是发现有些问题无法很好的解决,我在程序里面使用了dom4j来解析XML文件,因此需要把dom4j编译成C静态库link到我的应用程序上去,但是当我编译dom4j的时候,dom4j的有些class引用到了junit,编译不通过,还需要先编译junit,但是编译junit又发现别的包需要编译,并且有些class无论如何就编译不过。看来这些API的编译问题还是得留给GCJ去做了。

好了,费了这么多功夫,排除万难,终于把Java编译成exe了。上面的问题虽然头疼,还是可以解决的。但是接下来的问题就严重了。

我在自己的Java程序里面使用了Java的动态加载类的功能,比如说动态加载数据库驱动类库,动态加载外部配置文件。

先说动态加载类库,程序运行失败。分析一下原理,确实是不能动态加载的。因为Java程序是靠JVM使用Classloader来加载类库,现在JVM都没有了,怎么加载?当然行不通了。

看来解决办法只能是把要动态加载的类库预先编译成DLL,放在PATH路径下,让操作系统来动态加载DLL。
JVM --load--> Class Library
OS --load--> DLL

配置文件的动态加载大概只能另想办法了,只能在编译应用程序的时候就当做资源文件编译进去。不过这样也失去了可以动态修改配置文件的功能了。实在是得不偿失。

好吧,现在就算这些问题都可以忍受,可是对象序列化,从网络加载类库可怎么办?一个Java GUI的价值通常都做为C/S的客户端,要大量的和Java App Server通讯,交互数据,而和App Server打交道的主要方式就是RMI,以及基于RMI的EJB,这些通讯方式都完全变得不可能了,唯一能够顺利的方式只有基于文本的HTTP了,或许Web Services调用还可以,不过EJB调用是肯定行不通了。

经过一天的尝试,我发现通过GCJ的编译生成的exe程序成功了甩掉了JRE运行环境,达到了本地程序的境界,但是无比可惜的是,一个丢掉了JRE运行环境的Java程序就像没牙的老虎一样,也完全丧失了Java语言最强大的能力,彻底沦为了一个平庸的,功能受限的客户端图形编程语言。

JRE这个包含了JVM的运行环境赋予了Java程序无比强大的能力的同时,也不可避免的导致了Java运行速度缓慢。真是“成也萧何,败也萧何”。

所以看来,指望脱离JRE,让Java彻底本地运行是根本不切实际的,如果那样的话,还不如用VB,VC来的容易呢,何必这么费劲的用Java?
原文地址:https://www.cnblogs.com/huqingyu/p/63317.html