Java的性能有某种黑魔法之称。部分原因在于Java平台非常复杂,很多情况下问题难以定位。然而在历史上还有一种趋势,人们靠智慧和经验来研究Java性能,而不是靠应用统计和实证推理。在这篇文章中,我希望拆穿一些最荒谬的技术神话。
1.Java很慢
关于Java的性能有很多谬论,这一条是最过时的,可能也是最为明显的。
确实,在上世纪90年代和本世纪初处,Java有时是很慢。
相关厂商内容
JavaOne 2013再次回归,7月22-25日上海世博中心,精彩继续,5月31日前购票享半价!
2013年Esri空间信息技术开发者大会
GIS系列白皮书下载:云GIS平台介绍——ArcGIS Online
还记得Delphi么?Embarcadero携ER/Studio、RAD Studio XE和HTML5 Builder重装上阵
百度技术沙龙第三十八期:个性化推荐(2013年5月25日 周六)
然而从那以后,虚拟机和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开发者之中很流行。
如果未能透彻理解现有的架构,而且分析也已停顿,往往就是“缓存可以解决所有问题”这种谬论抬头的时候了。
在开发者看来,与其处理吓人的现有系统,还不如在前面加一层缓存,将现有系统隐藏起来,以此期待最好的情况。无疑,这种方式只是让整体架构更复杂了,当下一个接手的开发者打算了解系统现状时,情况会更糟糕。
规模庞大、设计拙劣的系统往往缺乏整体的设计,是一次一行代码、一个子系统这样写出来的。然而很多情况下,简化并重构架构会带来更好的性能,而且几乎总是更容易让人理解。
原文转自:http://www.infoq.com/cn/articles/9_Fallacies_Java_Performance