前言
起初 O'Reilly 公司让我写一本关于 Java 性能调优的书时,我还不确定是否值得写。我在想,难道 Java 性能调优我们做得还不够吗?事实上,虽然我日常的基本工作是 Java(和其他)应用程序的性能调优,但我宁愿将大多数时间都花在提高应用程序的算法效率以及处理外部系统的性能瓶颈上,而不是直接进行 Java 自身性能的调优。
但转念一想,我不禁哑然失笑(像往常一样)。的确,我的大量时间都花在了端到端的系统性能调优上,有时会发现那些原本可以用
却用了
算法的代码。不过这也说明,我每天考虑的都是 GC 调优、JVM 编译器的性能调优,或者是如何使 Java EE API 的性能发挥到极致。
说这些并不是想要抹杀过去 15 年里 Java 和 JVM 在性能上取得的巨大进步。1990 年代晚期,我在 Sun 公司担当 Java 布道师,当时仅有的真正意义上的“基准测试”工具来自 Pendragon 软件的 CaffeineMark 2.0。由于种种原因,该基准测试工具设计上的不足很快就限制了它的价值;然而在那个年代,我们总喜欢告诉所有人,依据这个基准测试,Java 1.1.8 的性能比 Java 1.0 快八倍。这并非耸人听闻——Java 1.1.8 已经有了真正的即时编译器,而 Java 1.0 差不多完全是解释型的。
之后,Java 标准委员会开始制定更严谨的基准测试,Java 的性能测试开始围绕这些基准测试展开。最终,JVM 的所有领域——垃圾收集、编译器和 API 都获得了长足的进步。这个过程一直延续到今天,而关于性能的一个重要事实是,调优正变得越来越艰难。引入即时编译器后所获得的八倍性能提升,只是一个简单的工程问题,即使编译器持续改进,我们也无法再次看到如此巨大的改进了。并行化垃圾收集也曾极大地提升过性能,但最近的变化则是渐进式的。
这是典型的应用发展过程(JVM 本身也是另外一个应用):在项目初期,很容易找到架构上的改进点(或代码缺陷),一旦找到就能极大改善性能。而在成熟应用中,要找到这样的性能改进点则很罕见。
起初我觉得,从很大程度上说,Java 性能调优都已经工程化了,但有几件事情让我相信我错了。首先,我每天在特定环境下运行 JVM 时,都会遇到许多这样或那样的问题。新工程师源源不断地进入 Java 领域。在特定的领域,JVM 的行为仍然相当复杂,因此有份描述它如何运作的指南很有必要。其次,现在的计算环境发生了变化,已经影响到了工程师们所面临的性能问题。
在过去几年中,性能关注点有了分歧。一方面,有大量内存堆可运行 JVM 的大机器现在已经很普遍了。为了应对这样的变化,JVM 也有了新的垃圾收集器(G1)——这项新技术相比传统的收集器更需要手工调优。同时,云计算又提升了单 CPU 的小机器的重要性:你可以从 Oracle、Amazon 或其他公司以非常便宜的价格租用单 CPU 机器,运行小的应用服务器。(你获得的并不是真的单 CPU 机器,而是一台巨大机器上的一个虚拟 OS 镜像,但虚拟 OS 被限制为使用单个 CPU。从 Java 角度看,它和单 CPU 的机器相同。)在这些环境中,正确管理小量内存变得非常重要。
Java 平台也在持续演变。Java 的每个新版本都会提供新的语言特性和新的 API,这些特性和 API 并不总是为了提高应用性能,也是为了改善开发人员的生产率。语言特性运用得好,应用的运行就会变得轻快,反之则缓慢笨重。另外,平台的演化也带来了一些重要的性能课题:毫无疑问,程序间用 JSON 交换信息要比用高度优化的私有协议更容易。节约开发人员的时间就是巨大的收益——但真正的目的是确保生产率提升的同时,性能也能提升(至少是两者之间取得平衡)。
