4.算法慢是性能问题的最普遍原因在程序员(和普通大众)中普遍存在一个错误观点就是他们总是理所当然地认为自己所负责的那部分系统才是最重要的。 就Java性能这个问题来说,Java开发者认为算法的质量是性能问题的主要原因。开发者会考虑如何编码,因此他们本性上就会潜意识地去考虑算法。 实际上,当处理现实中的性能问题时,算法设计占用了解决基本问题不到10%的时间。 相反,相对于算法,垃圾回收,数据库访问和配置错误会更可能造成程序缓慢。 大多数应用程序处理相对少量的数据,因此即使主算法有缺陷也不会导致严重的性能问题。因此,我们得承认算法对于性能问题来说是次要的;因为算法带来的低效相对于其他部分造成的影响来说是相对较小的,大多的性能问题来自于应用程序栈的其他部分。 因此我们的最佳建议就是依靠经验和产品数据来找到引起性能问题的真正原因。要动手采集数据而不是凭空猜测。 |
缓存能解决一切问题“关于计算机科学的每一个问题都可以通过附加另外一个层面间接的方式被解决” 这句程序员的格言,来至于David Wheeler(幸亏有因特网,至少有另外两位计算机科学家),是惊人的相似,特别是在web开发者中。 通常出现这种谬论是因为当面对一个现有的,理解不够透彻的架构时出现的分析瘫痪。 与其处理一个令人生畏的现存系统,开发者经常会选择躲避它通过添加一个缓存并且抱着最大的希望。当然,这个方法仅仅使整个架构变的更复杂,并且对试图理解产品架构现状的下一位开发者而言是一件很糟糕的事情。 夸大的说,不规则架构每次被写入一行和一个子系统。然而,在许多情况下,更简单的重构架构会有更好的性能,而且它们几乎也更易于被理解。 因此当你评估是不是需要缓存时,计划去收集基本用法统计(缺失率,命中率等)去证明实际上缓存层是个附加值。 |
6. 所有应用都要考虑到STW(译注:“stop-the-world” 机制简称STW,即,在执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集帮助器线程之外的线程都被挂起) Java平台的一个存在事实是,所有应用线程必须周期性的停止以便让垃圾搜集器GC运行。这有时被夸大为严重的弱点,即使是在缺少真实证据的情况下。 实证研究已经说明,人类通常无法察觉到频率超过每200毫秒一次的数字数据的变化(例如价格变动)。 因此对以人类作为首要用户的应用,一条有用的经验就是200毫秒或低于200毫秒的 Stop-The-World (STW)停顿通常无需考虑。有些应用(例如视频流)需要比这个更低的GC波动,但是很多GUI应用不是的。 有少数应用(比如低延迟交易,或者机械控制系统)对200毫秒停顿是不可接受的。除非你的应用属于那个少数,否则你的用户察觉到任何由垃圾回收带来的影响是不太可能的。 值得注意的是,在具有比物理内核更多应用线程的系统中,操作系统任务计划将会干涉对CPU的时间分片访问。Stop-The-World听起来吓人,但实际上,每个应用(无论是不是JVM)都必须处理对稀缺计算资源的内容访问。 如果不做测量,JVM的方法对应用性能带来的额外影响具有何等意义将无法看清。 总体来说,判断停顿的次数实际对应用的影响是通过打开GC日志的办法。分析此日志(或者手工,或者用脚本或工具)来确定停顿的次数。然后再判定这些是否确实给你的应用域带来问题。最重要的是,问自己一个最尖锐的问题:有用户确实抱怨了吗? |
7. 手工处理的对象池对很大范围内的应用都是合适的对Stop-The-World停顿的坏感觉引起一个常见的应对,即在java堆的范围内,为应用程序组发明它们自己的内存管理技术。经常这会归结为实现一个对象池(或甚至是全面引用计数)的方法,并且需要让任何使用了领域对象的代码参与进来。 这种技术几乎总是被误导。它通常具有自身久远以前的根源,那时对象定位代价昂贵,突然的变化被认为是不重要的。但现在的世界已经非常不同。 现代的硬件具有难以想象的定位效率;近来桌面或服务器硬件的内存容量至少达到了2到3GB。这是一个很大的数字;除了专业的使用情形,让实际的应用充满那么大的容量不是很容易。 对象池一般很难正确的实现(特别是有多个线程在工作的时候),并且有几个消极的要求使得把它作为一般场景使用成为一个艰难选择:
总之,只有在GC停顿不能被接受,而且在调试与重构过程中聪明的尝试也不能缩减停顿到可接受水平的时候,对象池才可以使用。 |