Java性能问题被冠以某种黑暗魔法的称谓。一部分是因为其平台的复杂性,在很多情况下,无法定位其性能问题根源。然而,在以前对于Java性能的技巧,有一种趋向:认为其由人们的智慧,经验构成,而不是应用统计和实证推理。在这篇文章中,我希望去验证一些最荒谬的技术神话。
在所有最过时的Java性能谬论当中,这可能是最明显的言论。
是的,在90年代和20年代初期,Java确实有点慢。
然而,在那之后,我们有超过10年的时间来改进虚拟机和JIT技术,现在Java整个体系的性能已经快的令人惊讶。
在6个单独的web性能测试基准中,Java框架占据了24个当中的22个前四的位置。
JVM性能分析组件的使用不仅优化了通用的代码路径,而且在优化那些严重领域也很有成效。JIT编译代码的速度在大多数情况下跟C++一样快了。
尽管这样,关于Java运行慢的言论还是存在,估计是由于历史原因造成的偏见,这个偏见来自当时那些使用Java早期版本的人们。
我们建议,在匆忙下结论之前先保留意见和评估一下最新的性能结果。
考虑以下一小段代码:
MyObject obj = new MyObject();
对一个Java开发者而言,很明显能看出这行代码需要分配一个对象和运行一个对应的构造方法。
从这来看,我们可以开始推出性能边界。我们知道有一些精确数量的工作必须继续,因此基于我们的推测,我们可以计算出性能影响。
这儿有个认知偏见,那就是根据以往的经验,任何工作都需要被做。
实际上,javac和JIT编译器都可以优化无效代码。就拿JIT编译器来说,代码甚至可以基于数据分析而被优化掉,在这种情况下,该行代码将不会被运行,因此它就不会有什么性能方面的影响。
而且,在一些Java虚拟机(JVM)中,例如JRockit中,即使代码路径没有完全失效,JIT编译器为了避免分配对象甚至可以执行分解对象操作。
这段文字在这里的意义就是当处理Java性能方面的问题时,上下文很重要。而且过早的优化可能会产生意料之外的结果。所以为了获得最好的结果,不要试图过早的优化。与其不断构建你的代码不如用性能调整技术去定位并且改正代码性能的潜在危险区。
“关于计算机科学的每一个问题都可以通过附加另外一个层面间接的方式被解决”
这句程序员的格言,来至于David Wheeler(幸亏有因特网,至少有另外两位计算机科学家),是惊人的相似,特别是在web开发者中。
通常出现这种谬论是因为当面对一个现有的,理解不够透彻的架构时出现的分析瘫痪。
与其处理一个令人生畏的现存系统,开发者经常会选择躲避它通过添加一个缓存并且抱着最大的希望。当然,这个方法仅仅使整个架构变的更复杂,并且对试图理解产品架构现状的下一位开发者而言是一件很糟糕的事情。
夸大的说,不规则架构每次被写入一行和一个子系统。然而,在许多情况下,更简单的重构架构会有更好的性能,而且它们几乎也更易于被理解。
因此当你评估是不是需要缓存时,计划去收集基本用法统计(缺失率,命中率等)去证明实际上缓存层是个附加值。
(译注:“stop-the-world” 机制简称STW,即,在执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集帮助器线程之外的线程都被挂起)
Java平台的一个存在事实是,所有应用线程必须周期性的停止以便让垃圾搜集器GC运行。这有时被夸大为严重的弱点,即使是在缺少真实证据的情况下。
实证研究已经说明,人类通常无法察觉到频率超过每200毫秒一次的数字数据的变化(例如价格变动)。
因此对以人类作为首要用户的应用,一条有用的经验就是200毫秒或低于200毫秒的 Stop-The-World (STW)停顿通常无需考虑。有些应用(例如视频流)需要比这个更低的GC波动,但是很多GUI应用不是的。
有少数应用(比如低延迟交易,或者机械控制系统)对200毫秒停顿是不可接受的。除非你的应用属于那个少数,否则你的用户察觉到任何由垃圾回收带来的影响是不太可能的。
值得注意的是,在具有比物理内核更多应用线程的系统中,操作系统任务计划将会干涉对CPU的时间分片访问。Stop-The-World听起来吓人,但实际上,每个应用(无论是不是JVM)都必须处理对稀缺计算资源的内容访问。
如果不做测量,JVM的方法对应用性能带来的额外影响具有何等意义将无法看清。
总体来说,判断停顿的次数实际对应用的影响是通过打开GC日志的办法。分析此日志(或者手工,或者用脚本或工具)来确定停顿的次数。然后再判定这些是否确实给你的应用域带来问题。最重要的是,问自己一个最尖锐的问题:有用户确实抱怨了吗?
对Stop-The-World停顿的坏感觉引起一个常见的应对,即在java堆的范围内,为应用程序组发明它们自己的内存管理技术。经常这会归结为实现一个对象池(或甚至是全面引用计数)的方法,并且需要让任何使用了领域对象的代码参与进来。
这种技术几乎总是被误导。它通常具有自身久远以前的根源,那时对象定位代价昂贵,突然的变化被认为是不重要的。但现在的世界已经非常不同。
现代的硬件具有难以想象的定位效率;近来桌面或服务器硬件的内存容量至少达到了2到3GB。这是一个很大的数字;除了专业的使用情形,让实际的应用充满那么大的容量不是很容易。
对象池一般很难正确的实现(特别是有多个线程在工作的时候),并且有几个消极的要求使得把它作为一般场景使用成为一个艰难选择:总之,只有在GC停顿不能被接受,而且在调试与重构过程中聪明的尝试也不能缩减停顿到可接受水平的时候,对象池才可以使用。
Oracle JDK默认使用一个并行的,全部停止(stop-the-world STW)垃圾收集器来收集老年代的垃圾。
另外一个选择是并发标记清除(CMS)收集器。这个收集器允许程序线程在大部分的GC周期中仍然继续工作,但它需要付出一些代价和带来一些警告。
允许程序线程和GC线程一起运行不可避免地导致对象表的变异同时又影响到对象的活跃性。这不得不在发生后进行清楚,所以CMS实际上有两个STW阶段(通常非常短)。
这会带来一些后果:
依据程序的情况这些成本或者是值得的或者又不是。但并没有免费的午餐。CMS收集器是一个卓越的工程品,但它不是万能药。
所以在介绍前,CMS是你正确的GC策略,你得首先考虑Parallel Old的STW是不可接收的和不能调和的。最后,(我不能足够地强调),确定所有的指标都从相当的生产系统上得到。
当一个应用程序崩溃,GC中止运行时,许多应用组会通过增加堆内存来解决问题。在许多情况下,这可以很快解决问题,并争取时间来考虑出一个更深的解决方案。然而,在没有真正理解性能产生的根源时,这种解决策略实际上会使情况更糟糕。
试想一下,一个编码很烂的应用构造了非常多的领域对象(生命周期大概维持2,3秒)。如果内存分配率足够高,垃圾回收就会很快地执行,并把这些领域对象放到年老代。一旦进入了老年代,对象就会立即死去,但直到下一次完全回收才会被垃圾回收器回收。
如果这个应用增加其堆内存,那么我们能做的是增加空间,为了存放那些相对短期存在,然后消逝的领域对象。这会使得 Stop-The-World 的时间更长,对应用毫无益处。
在改变堆内存和或其他参数之前,理解一下对象的动态分配和生命周期是很有必要的。没做调查就行动,只会使事情更糟。在这里,垃圾回收器的老年分布信息是非常重要的。
评论删除后,数据将无法恢复
评论(180)
引用来自“xesam”的评论
引用来自“幸福线”的评论
就凭打开eclipse的速度,我就不可能相信java快
引用来自“lplus”的评论
Java总是觉得硬件能解决速度,理论上是可以无限提升,只要硬件都是可以无限提升的,这句话说了多少年了,我酷睿I7,16G内存,做为一个个人电脑还不够吗,可并没有明显的提升1.在项目开发中,java程序员易于培养且工资较低
2.项目维护成本低(java项目在开发中代码可读性强于其他语言)
3.项目增加服务内容或二次开发时,其时间及开发成本远低于其他语言
4.在手机应用上与ios二分天下,android是以java为基础的(windows phone只能拼命追赶)
5.随着技术的进步,硬件(cpu及内存)的限制将逐渐不是问题
6.云技术逐渐成熟将进一步解放性能限制
7.java语言更新换代中有很多大公司在做推手,而且java程序员人数很多,在遇到问题时容易解决
8.java 开源
以上优势还没有哪一种语言能够媲美,所以一般大公司的项目主体是java,而对性能要求较高的程序才会用C进行开发。未来的趋势将是Java和C的天下,这一趋势无人能挡。(除非出现一位计算机天才开发出比java更好的超越以上优势的语言,并且有大公司愿意支持,java才有可能退下神坛)
---- LyonQian--2016-06-01
引用来自“breakerror”的评论
sb java把中国人的计算机能都排挤到底层之外的地方。直接架空了中国的人。工程上面用用还说的过去,如果大面积推广给年轻人,中国的 计算机科学将没有未来了!!!引用来自“xesam”的评论
引用来自“幸福线”的评论
就凭打开eclipse的速度,我就不可能相信java快
引用来自“鳄鱼先生”的评论
引用来自“breakerror”的评论
引用来自“鳄鱼先生”的评论
引用来自“HooxinFirefoxmmx”的评论
引用来自“鳄鱼先生”的评论
实话讲,现在还在追捧C++的程序员绝对是脑残。
我做过10年C++开发,从来不觉得C++比Java有啥明显的优势,除了工资方面。
引用来自“鳄鱼先生”的评论
引用来自“李珍珍”的评论
引用来自“鳄鱼先生”的评论
引用来自“李珍珍”的评论
java 内存占用量很大 没错吧 ,, 这才是它的致命缺陷
实话讲到现在还想不通还有那么多人追捧C++。
引用来自“唐海康”的评论
引用来自“breakerror”的评论
引用来自“ichenshy”的评论
引用来自“阿尔法兽”的评论
引用来自“ichenshy”的评论
引用来自“阿尔法兽”的评论
引用来自“ichenshy”的评论
把Java运行慢归结于“历史原因造成的偏见”,实在太可笑了!说Java慢的,一般都是由另一门语言转到Java后的主观感受(也有人做比较测试),并不是什么偏见!!现在Java的运行速度的确有提升,除了“有超过10年的时间来改进虚拟机和JIT技术”,还有硬件的飞速发展,而且这个因素占了很大比重。为什么把Java的执行效率跟CPU和内存占用一起说呢? 由于服务器的硬件提升已经非常大,所以在主观上很难感觉到Java慢,需要做对比测试,用数据来比较,但如果是面向用户的桌面应用,主观感受一个字——慢!两个字——笨重!
http://developer.51cto.com/art/201007/211089.htm
http://www.keakon.net/2009/12/07/Java%E3%80%81PHP%E3%80%81Python%E4%B8%8EMySQL%E4%BA%A4%E4%BA%92%E7%9A%84%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95
跟Java做性能对比测试的,这么多年我都不知道看过多少了,有国外国内,有的测试设计很好,有的设计欠周全,stackoverflow也经常有这方面的讨论,但总体上来说,Java一般都不占什么优势!
引用来自“唐海康”的评论
引用来自“breakerror”的评论
引用来自“ichenshy”的评论
引用来自“阿尔法兽”的评论
引用来自“ichenshy”的评论
引用来自“阿尔法兽”的评论
引用来自“ichenshy”的评论
把Java运行慢归结于“历史原因造成的偏见”,实在太可笑了!说Java慢的,一般都是由另一门语言转到Java后的主观感受(也有人做比较测试),并不是什么偏见!!现在Java的运行速度的确有提升,除了“有超过10年的时间来改进虚拟机和JIT技术”,还有硬件的飞速发展,而且这个因素占了很大比重。为什么把Java的执行效率跟CPU和内存占用一起说呢? 由于服务器的硬件提升已经非常大,所以在主观上很难感觉到Java慢,需要做对比测试,用数据来比较,但如果是面向用户的桌面应用,主观感受一个字——慢!两个字——笨重!
http://developer.51cto.com/art/201007/211089.htm
http://www.keakon.net/2009/12/07/Java%E3%80%81PHP%E3%80%81Python%E4%B8%8EMySQL%E4%BA%A4%E4%BA%92%E7%9A%84%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95
跟Java做性能对比测试的,这么多年我都不知道看过多少了,有国外国内,有的测试设计很好,有的设计欠周全,stackoverflow也经常有这方面的讨论,但总体上来说,Java一般都不占什么优势!