Java的性能有某种黑魔法之称。部分原因在于Java平台非常复杂,很多情况下问题难以定位。然而在历史上还有一种趋势,人们靠智慧和经验来研究Java性能,而不是靠应用统计和实证推理。在这篇文章中,我希望拆穿一些最荒谬的技术神话。 1.Java很慢关于Java的性能有很多谬论,这一条是最过时的,可能也是最为明显的。 确实,在上世纪90年代和本世纪初处,Java有时是很慢。然而从那以后,虚拟机和JIT技术已经有了十多年的改进,Java的整体性能现在已经非常好了。 在6个独立的Web性能基准测试中,Java框架在24项测试中有22项位列前四。 尽管JVM利用性能剖析仅优化常用的代码路径,但这种优化效果很明显。很多情况下,JIT编译的Java代码和C++一样快,而且这样的情况越来越多了。
尽管如此,依然有人认为Java平台很慢,这或许源自体验过Java平台早期版本的人的历史偏见。 在下结论之前,我们建议保持客观的态度,并且评估一下最新的性能结果。 2.可以孤立地看待单行Java代码考虑下面这行短小的代码: MyObject obj = new MyObject(); 对Java开发者而言,看似很明显,这行代码一定会分配一个对象并调用适当的构造器。 我们也许可以据此推出性能边界了。我们认为这行代码一定会导致执行一定量的工作,基于这种推定,就可以尝试计算其性能影响了。 其实这种认识是错误的,它让我们先入为主地认为,不管什么工作,在任何情况下都会进行。 事实上,javac和JIT编译器都能够将死代码优化掉。就JIT编译器而言,基于性能剖析数据,甚至可以通过预测将代码优化掉。在这样的情况下,这行代码根本不会运行,所以不会影响性能。 此外,在某些JVM中——比如JRockit——JIT编译器甚至可以将对象上的操作分解,这样即便代码路径还有效,分配操作也可以避免。 这里的寓意是,在处理Java性能问题时,上下文非常重要,过早的优化有可能产生违反直觉的结果。所以最好不好过早优化。相反,应该总是构建代码,并且使用性能调校技术来定位性能热点,然后加以改进。 3.微基准测试和你想象的一样正如我们上面看到的那样,检查一小段代码不如分析应用的整体性能来的准确。 尽管如此,开发者还是喜欢编写微基准测试。似乎对平台底层的某些方面进行修修补补会带来无穷的乐趣。 理查德·费曼曾经说过:“不要欺骗自己,你自己正是最容易被欺骗的人。”这句话用来说明编写Java微基准测试这件事是再合适不过了。 编写良好的微基准测试极其困难。Java平台非常复杂,而且很多微基准测试只能用于测量瞬时效应,或是Java平台的其他意想不到的方面。 例如,如果没有经验,编写的微基准测试往往就是测一下时间或垃圾收集,却没有抓住真正的影响因素。 只有那些有实际需求的开发者和开发团队才应该编写微基准测试。这些基准测试应该完全公开(包括源代码),而且是可以复现的,还应接受同行评审及进一步的审查。 Java平台的很多优化表明统计运行和单次运行对结果影响很大。要得到真实可靠的答案,应该将一个单独的基准测试运行多次,然后把结果汇总到一起。 如果读者感觉有必要编写微基准测试,Georges、Buytaert和Eeckhout等人的论文《利用严格的统计方法评测Java 性能(Statistically Rigorous Java Performance Evaluation)》是个不错的开始。缺乏适当的统计分析,我们很容易被误导。 有很多开发好的工具以及围绕这些工具的社区(比如Google的Caliper)。如果确实有必要编写微基准测试,那也不要自己编写,这时需要的是同行的意见和经验。 4.算法慢是性能问题的最常见原因在开发者之间有一个很常见的认知错误(普通大众也是如此),即认为系统中他们控制的那部分很重要。 在探讨Java性能时,这种认知错误也有所体现:Java开发者认为算法的质量是性能问题的主要原因。开发者考虑的是代码,因此他们自然会偏向于考虑自己的算法。 实际上在处理一系列现实中的性能问题时,人们发现算法设计是根本问题的几率不足10%。 相反,与算法相比,垃圾收集、数据库访问和配置错误导致应用程序缓慢的可能性更大。 大部分应用处理的数据量相对较小,因此,即使主要算法效率不高,通常也不会导致严重的性能问题。可以肯定,我们的算法不是最优的;尽管如此,算法带来的性能问题还是算小的,更多性能问题是应用栈的其他部分导致的。 因此我们的最佳建议是,使用实际生产数据来揭开性能问题的真正原因。要测量性能数据,而不是凭空猜测! 5.缓存可以解决所有问题“计算机科学中的所有问题都可以通过引入一个中间层来解决。” David Wheeler的这句程序员格言(在互联网上,这句话至少还被认为是其他两位计算机科学家说的)非常常见,尤其是在Web开发者之中很流行。 如果未能透彻理解现有的架构,而且分析也已停顿,往往就是“缓存可以解决所有问题”这种谬论抬头的时候了。 在开发者看来,与其处理吓人的现有系统,还不如在前面加一层缓存,将现有系统隐藏起来,以此期待最好的情况。无疑,这种方式只是让整体架构更复杂了,当下一个接手的开发者打算了解系统现状时,情况会更糟糕。 规模庞大、设计拙劣的系统往往缺乏整体的设计,是一次一行代码、一个子系统这样写出来的。然而很多情况下,简化并重构架构会带来更好的性能,而且几乎总是更容易让人理解。 所以当评估是否真的有必要加入缓存时,应该先计划收集一些基本的使用统计信息(比如命中率和未命中率等),以此证明缓存层带来的真正价值。
|