Linux Java 实现方案对比
鉴于 Linux 和 Java 目前受到的高度关注,两者的结合在 1 到 10 的炒作等级中稳居 12。然而,作为一名工程师——因此也是一位天生的怀疑论者——我更喜欢硬性事实而不是营销口号。因此,我着手创建一个简单的基准测试程序,以比较 Linux 的不同 Java 实现方案。
无论炒作如何,我认为 Java 最重要的贡献在于它本身就是一种设计非常精良的编程语言,大多数专业人士都喜欢使用。Java 的一些特性使其对软件工程师具有吸引力,包括:
Java 是一种“纯粹”的面向对象编程语言(与 C++ 这种混合型语言不同),具有简单而优雅的对象模型。根据您的观点,这可能是一个优点或缺点。我个人的经验是,如果新手程序员使用 Smalltalk、Eiffel 或 Java 等“纯粹”的面向对象语言,而不是 C++ 等混合型语言,他们上手会更快。
Java 避免了 C++ 的许多复杂性,从而使程序更不易出错,并提高了程序员的生产力。例如,在 Java 中,只有一种创建新对象的方式:调用 new 运算符并获取对该对象的引用(如果您喜欢,也可以称为指针)。相比之下,C++ 中创建对象的方式有很多种。
Java 不需要预处理器,因此免受“宏泛滥”和无休止嵌套的包含文件的影响。
Java 具有垃圾回收器,用于释放不再使用的对象所消耗的内存。在 C 和 C++ 中,必须花费大量的设计和编程工作在内存分配和释放方案上。Java 中释放对象是自动的(就像在 Smalltalk 和 Eiffel 中一样)。
Java 语言规范定义了 int 和 float 等基本数据类型的大小,因此无论您的程序在哪个平台上运行,int 始终为 32 位。您在 C 程序中是否经常隐式地假设指针的大小与整数相同,并且当您将程序移植到指针大小与整数大小不同的架构时,是否为此付出了代价?
最后,Java 内置了对多线程和多线程同步的支持,并开箱即用地提供了一个庞大的类库(尽管包括我自己在内的一些人认为该库最近变得过于庞大)。
Java 仍然不是一种标准化的语言,而且它是否会成为标准化语言也令人怀疑。Sun 公司对语言和库中包含什么以及不包含什么拥有最终决定权,就这么简单。“Java 2 平台”或简称为 Java 开发工具包 (JDK) V1.2 在其库中大约有 1200 个类。Sun 在 Solaris/SPARC、Solaris/Intel、Windows-32 以及最近的 Linux 上也发布了其 JDK。IBM 最近发布了适用于 Linux 的 JDK 1.3 实现方案。我在与其他所有基准测试相同的硬件和操作系统上使用此 JDK 运行了基准测试,并得到了以下数字:
运行时间:1384 毫秒对象/毫秒:361java-version 输出:Classic VM (J2RE 1.3.0 IBM buildcxdev-20000502 (JIT 已启用:jitc))
Sun 的 JDK 通过依赖于一种称为“字节码”的与架构无关的中间代码来实现 Java 程序的平台独立性,字节码在每个目标机器上进行解释。解释器称为“Java 虚拟机”或简称 JVM。
由于解释执行速度较慢,因此大多数 JVM 都配备了即时编译器 (JIT)。JIT 在运行时(即在解释器运行时)将字节码转换为机器代码。生成的机器代码存储在内存中,并在解释器终止时丢失。一般来说,纯解释器显示出更快的程序启动时间,而带有 JIT 的 JVM 启动时间较长(因为它编译字节码);但是一旦程序启动并运行,它就比解释执行的程序更快。可以对解释器和 JIT 编译器进行许多优化。Sun 的“Hotspot”JVM 目前尚未在 Linux 下提供,但最终应该会提供,它是尝试从两个世界中获得最佳效果的一种尝试。
最后,Java 语言规范中没有任何内容阻止标准编译器技术的应用,即直接将 Java 源代码编译为机器代码。GNU 编译器系统的 Java 前端就是这样做的。
在撰写本文时,Linux 有相当多的 Java 实现方案可用。以下是我知道的并且能够正常工作的方案:
Sun JDK 的 Blackdown 移植版,版本 1.2.2。我测试了这个移植版的候选发布版本 4,它包括解释器和 JIT。JIT 是 Sun 随 Solaris 版 JDK 提供的 JIT 的移植版。默认情况下,JIT 是启用的,但可以使用命令行开关将其关闭。此移植版附带的文档警告说,JIT 尚不完全可靠。我从众多 Blackdown FTP 镜像站点之一下载了这个移植版,这些镜像站点可以从 Blackdown 的主页 http://www.blackdown.org/ 访问。
Sun 自己的 Sun JDK 的 Blackdown 移植版,版本 1.2.2。最近,Sun 开始在其自己的网站上发布 Linux 版 JDK。据我所知,此移植版与 Blackdown 团队发布的版本相同。但至少有一个明显的区别:Sun 的版本不带 JIT;它只是一个解释器。但是,Sun 建议使用 Borland 开发的 JIT。我从 Sun 的 Javasoft 网站 http://www.javasoft.com/ 下载了这个移植版。
Borland 为 Sun 和 Blackdown 移植版 JDK 1.1.2 提供的 JIT。这不是一个完整的 Java 开发工具包;它只是一个 JIT。它可以与 Sun JDK 1.2.2 的 Blackdown 移植版以及 Sun 发布的 Linux JDK 1.2.2 一起使用。我从 Borland 的网站 http://www.borland.com/ 下载了这个 JIT,它是一个大约 170KB 的简单共享库。
Sun JDK 1.1.8 的 Blackdown 移植版。我测试了这个移植版的版本 1,它只包含一个解释器(与带有 JIT 的 Windows 版本不同)。
IBM 的 Linux 版 JDK 1.1.8。此 JDK 以“非常快速”且非常稳定而闻名。它带有一个 JIT,默认情况下是启用的,但可以使用命令行开关将其关闭。我从 IBM 的网站 http://www.ibm.com/ 下载了这个 JDK。
Transvirtual Technologies 的 Kaffe 版本 1.0b4。Kaffe 由 Tim Wilkonson 和其他人从头开始开发,没有任何来自 Sun 的代码。我使用的 Kaffe 版本与 Sun 的 JDK 1.1 兼容。我使用了 Red Hat 6.1 CD-ROM 上的 Kaffe 包;但是,有一个专门介绍 Kaffe 开源版本的网站,网址为 http://www.kaffe.org/。Kaffe 可用于各种 UNIX 版本和处理器架构,而不仅仅是 x86 处理器上的 Linux。我使用的 Kaffe 版本包含一个 JIT,它始终处于开启状态,或者至少,我无法弄清楚如何关闭它。
Cygnus Support 随其 Codefusion-1.0 开发环境提供的本机 Java 编译器。此编译器是 EGCS(Experimental GNU Compiler System,实验性 GNU 编译器系统)的增强版本,尽管您很难再将这款高质量的编译器称为“实验性”编译器了。与我研究的所有其他 Java 实现方案不同,此编译器生成与从 C 和 C++ 源代码文件创建的对象文件链接兼容的本机代码。该编译器附带一个库,其中包含 Java 程序所需的运行时支持,其中包括垃圾回收器等。我使用的 EGCS 版本(“2.9-codefusion-990706”)不是免费的;您必须从 Cygnus 购买。他们的网站是 www.cygnus.com/ 或 www.redhat.com。
让我们从免责声明开始本节:编写有意义的基准测试程序非常困难,并且没有哪个单一的基准测试可以公正地评估像 Java 实现方案这样复杂系统的所有方面。我使用的基准测试也不例外。它没有解决许多问题;例如,它不涵盖多线程问题、数据库访问或图形性能。
话虽如此,让我们来看看基准测试。从技术角度来看,基准测试程序所做的事情是大多数面向对象程序所做的:创建对象并在其上调用方法。更具体地说,基准测试创建了 50 万个非常简单的帐户对象,向每个创建的对象添加一个金额,然后将所有对象的金额相加。基准测试的结果是创建和处理所有对象所花费的时间,以及由此得出的每毫秒创建和处理的对象数量。
我在运行 Red Hat Linux 6.1 的 Dell Latitude CP 笔记本电脑(配备 233MHz CPU 和 128MB RAM)上运行了所有基准测试。基准测试程序的代码在清单 1 中给出。
正如已经说过的,这个基准测试并不完美,但我认为它确实给出了相同平台上不同 Java 实现方案相对性能的指示。
现在来看结果。JIT 通常比解释器快,本机代码甚至比 JIT 更快,这不足为奇。表 2 总结了结果。
有一些观察结果我认为值得一提。首先,Java 1.2 实现方案通常比 Java 1.1 实现方案更快。除了 Java 1.2 提供的更多功能外,这也是选择 Java 1.2 而不是 Java 1.1 的有力论据。其次,目前最快的 Java 1.2 实现方案是启用 JIT 的 Blackdown 移植版。Borland 提供的 JIT 并不能加快速度,至少在此基准测试中是如此。第三,如果您必须坚持使用 Java 1.1(例如,出于兼容性原因),那么您目前最好的选择是 Kaffe 1.0b4 和启用 JIT 的 IBM JDK 1.1.8。第四,如果您不需要 Java 字节码和 Java 1.2,那么最快的选择是使用 EGCS 的 Java 前端。我无法测试带有 gcj 的 EGCS 开源版本,但我怀疑它的性能与 Cygnus/Red Hat 商业销售的版本相差几个百分点。
最后,我在相同的硬件上使用 Sun 的 JDK 1.2.2 在 Windows NT 4.0 Service Pack 5 上运行了基准测试。对于 Linux 爱好者来说,结果令人失望。基准测试报告,启用 JIT 时每毫秒处理 198 个对象,在解释器模式下每毫秒处理 133 个对象。换句话说,Windows 上的 Java 解释器比 Linux 上可用的最快 JIT 快,而 Windows JIT 比 Linux 上的本机代码快。只要 Linux 上的 Java 性能不如 Windows,我认为 Linux 就不会成为 Java 开发人员的首选平台。让我们希望在不久的将来,像 Sun 或 IBM 这样的公司至少投入与现在调整 Windows 上 Java 相同的时间来调整 Linux 上的 Java。
作为一名长期的 C 和 C++ 程序员,我无法抗拒用 C++ 编写类似基准测试的诱惑。清单 2 包含 C++ 版本基准测试的代码。
我使用 Red Hat 6.1 (EGCS 2.91.66) 附带的标准 C++ 编译器和 Cygnus Codefusion 1.0 开发环境中包含的 C++ 编译器编译了 C++ 基准测试程序。表 3 显示了 C++ 基准测试的结果。
基准测试表明,可比的 C++ 程序仍然比最快的 Java 实现方案(gcj 本机代码编译器)快 3 倍,甚至比最快的 Java JIT 快 4.3 倍。
如果这个简单的基准测试有什么结论的话,那就是:我对 Java 作为一种编程语言(即其设计)印象深刻,但对于目前 Linux 或任何其他平台上的 Java 实现方案,我并没有太大的印象。
Java 实现方案是计算机领域最古老的定律之一的很好例证,该定律指出软件变慢的速度快于硬件变快的速度。毫无疑问,Java 有其用武之地,而且很多时候——事实上,更多时候——它在程序员生产力方面的优势超过了与 C++ 相比在性能方面的劣势。购买速度快四倍的 CPU 通常比花费两倍的时间来开发应用程序更便宜。至少,对于定制开发的系统来说是这样。嵌入式系统或拥有数十万份副本的大众市场软件则是完全不同的情况。在 100 美元的设备中节省 5 美元的 CPU 芯片可能会产生巨大的差异,并为增加的开发时间买单。
毫不奇怪,Java 今天主要用于定制开发的企业应用程序,在这些应用程序中,开发成本和时间至关重要。只要 Java 实现方案的性能达不到可比 C++ 应用程序性能的 80%,我认为我们就不会看到用 Java 编写的现成产品,例如文字处理器或电子表格。
如果能有一种兼具 Java 的优雅和简洁以及 C 的效率的编程语言,那岂不是很好吗?好吧,也许这将是下一个大型开源项目。
本文中引用的所有清单都可以通过匿名下载文件 ftp.linuxjournal.com/pub/lj/listings/issue76/4005.tgz 获取。
Michael Hirsch 可以通过电子邮件 Hirsch.Michael@acm.org 联系。