编者注:本文介绍的内容主要是基于 Fedora4 版本,尽管现在 Fedora 已经发布了 8 版本,但经过内部讨论我们还是决定发布这篇文章,原因在于本文提供了一个很好的开发思路,相信对读者会有帮助!
在 Fedora 的发布版中,Fedora4 是首个包含了大量用 Java 编程语言编写的代码的版本。正是由于 GNU 类路径(Classpath)和 GNU gcj(GNU Compiler for Java)的改进才促成了这些附加部分。
GCJ 基础
首先,GNU gcj 不是 Java。
然而,gcj 的目标是实现一个完整的系统,该系统兼容于 Java 并且将预编译器(ahead-of-time compiler)置于中心。它拥有一个基于 GNU 类路径的净化类库和一个内置的解释器。其编译器可以将 Java 源文件、类文件、甚至是整个 jar 文件编译成目标码。
以前 gcj 对待 Java 采取的是一种“激进且传统”的方式,它认为 Java 好像是 C++ 的某个不常用的方言似的。这么做有好的一面,然而不幸的是两者的运行时链接模型差异太大——因此当遇到一些规模比较大、复杂度比较高的 Java 应用时,这种方法就无能为力了,特别是面对那些有着复杂的类加载机制的 Java 应用时,显得尤为突出。
在 GCC4.0 的发布版中,我们对 gcj 实现了一种新的编译方式,称之为二进制兼容性 ABI(Binary Compatibility Application Binary Interface)。这种编译方式将所有的链接推迟到运行时刻进行并且完全实现了 Java 的二进制兼容规范——正好是让预编译的代码与类装载结合所需要的。
我们还增加了一个类映射数据库。在运行时,只要我们定义好一个类,gcj 运行时(叫做“libgcj”)就会在数据库中寻找这个类。如果找到该类(注意我们这里使用的是类的“内容”,而不仅仅是类名),那么 libgcj 就会映射到该共享库中,该库包含了编译后的类。
这两个改变使得我们可以做一些更强大的事情:我们可以预编译 Java 程序而不必要求任何应用级的改变。此外,由于采用了新的方式对字节码进行校验,我们还能确保编译的代码在运行时的类型安全。
构建 RPM
在 Fedora Core 上构建 Java 应用是很简单的——现存的构建方式不会发生变化。Fedora Core 舶来了“Ant“并且使用来自 Eclipse 的 Java 编译器将 Java 代码编译成字节码。
描述如何编写 RPM 已经超出了本文讨论的范围,但是 Fedora RPM 指南上有一些有用的信息,JPackage 上也有一些 Java 特定的指南。
一旦你的程序被编译成字节码,你就可以将他们编译为本地代码。因为 gcj 尚不包含一个即时编译器(JIT),这就是其获得合理性能的方法。在某些情况下,其性能可能会超过已有即时编译器,因为 gcj 使用了共享库……当你同时运行应用程序的多个实例时,你就会看到这种巨大的差异。
Fedora 提供了两个程序,使得本地编译包的工作变得更简单。我们在构建 RPM 时会使用到他们。
第一个程序是“aot-compile-rpm”。它会搜索 jar 文件并且使用 gcj 将他们编译到共享库中。aot-compile-rpm 知道一些 gcj 特定的技巧,例如在编译前将比较大的 jar 文件分割为若干个小的文件(在运行时编译一个大的 jar 文件将耗费大量内存资源),在链接结果共享库时使用–Bsymbolic(这会导致运行时性能改善)。
假如你没有在构建 RPM,那么一个替代方案就是直接将程序中的 jar 文件编译到共享库中。这里我展示一个最简单的方法(我之前提到过,对于一个大的 jar 文件,这样做会非常慢):
gcj -fjni -findirect-dispatch -fPIC -shared \
-Wl,-Bsymbolic -o foo.jar.so
foo.jar
分解一下:
- -fjni 告诉 gcj 在 Java 代码中的本地方法(native methods)是用 JNI 实现的。
- -findirect-dispatch 告诉 gcj 使用二进制兼容性 ABI(Application Binary Interface)。
- 当构建一个共享库时我们需要使用 -fPIC 和 -shared
- -WI 和 -Bsymbolic 告诉链接器在可能的情况下绑定共享库中的引用。
Fedora Core 提供的第二个有用的程序是 rebuild-gcj-db。当安装或者卸载一个 RPM 改变了全局类映射数据库时,我们将使用该程序,并且该程序应该运行在 RPM 的 %post 和 %postun 部分。
rebuild-gcj-db 根据约定来运转——它假设每个包在目录 /usr/lib/gcj(对于多架构操作系统中 64 位的包,目录为 /usr/lib64/gcj,有相应的 RPM 宏去解决这个问题)下的某个地方安装自己的类映射数据库。然后它循环遍历所有的这些个体数据库并且把他们整合到运行时使用的全局数据库中。
注意到使用 gcj 去编译一个 Java 程序并不总是能成功的。Gcj 的类库尚不完整,有时程序会使用到某些尚未实现的 API。例如,Swing 目前仍在积极开发中。同样,尽管 Sun 公司已经发布了警告,一些包使用了私有的com.sun.* 或者sun.* 下的APIs,——然而一般来说,gcj 并没有实现他们。
谁在使用?
Fedora Core 4 使用了 gcj 来为很多应用程序进行编译。
- 首先,Fedora 包含了 ant 以及 ant 的很多依赖,以此来编译和运行其他 Java 程序。它也包含了 Eclipse 的 Java 编译器。
- Tomcat 及其依赖。
- OpenOffice 中的 Java 代码。
- Eclipse IDE 和一些插件,比如 CDT。
Fedora5 中的新面孔:
JOnAS 应用服务器,一个 J2EE 实现,也在进行中但是它还尚未通过 Fedora Extras 的审查。
接下来的工作
在过去的一年中,GNU Classpath 社区向着目标大踏步地前进,我们期望在 2006 年能继续保持这个势头。我们有一个经常更新的 API 比较页面;你可以在这里追踪我们API 的状态。
我们也在为gcj 的核心改进而努力工作着:将编译器,运行时和类库更新到Java5。
最后,我们正在libgcj 里实现Java 的安全架构。这会实现一个 Mozilla 的插件和 netx (Java Web Start 的一个实现)。
个人简介
Tom Tromey 于 1990 年毕业于加利福尼亚理工学院。现主要从事 Red Hat 下的 GNU Java 编译器和运行时开发工作。他编写了 GNU Automake。
查看英文原文: Delivering Java Apps on Fedora Core - - - - - -
译者简介:张龙,同济大学软件工程硕士,现就职于理光软件研究所。主要从事文档工作流和办公自动化解决方案的研发工作。热衷于 Java 轻量级框架的研究,对敏捷方法很感兴趣。曾有若干年的 J2EE 培训讲师经历。参与 InfoQ 中文站内容建设,请邮件至 china-editorial[at]infoq.com 。
评论