Java编程思想第4版[中文版](PDF格式)-第176部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
销。
■同步:在JDK 解释器中,调用同步方法通常会比调用不同步方法慢 10 倍。经 JIT 编译器处理后,这一性能
上的差距提升到50 到 100倍(注意前表总结的时间显示出要慢97 倍)。所以要尽可能避免使用同步方法—
—若不能避免,方法的同步也要比代码块的同步稍快一些。
■重复利用对象:要花很长的时间来新建一个对象(根据前表总结的时间,对象的新建时间是赋值时间的
980 倍,而新建一个小数组的时间是赋值时间的3100 倍)。因此,最明智的做法是保存和更新老对象的字
段,而不是创建一个新对象。例如,不要在自己的 paint()方法中新建一个Font 对象。相反,应将其声明成
实例对象,再初始化一次。在这以后,可在paint()里需要的时候随时进行更新。参见 Bentley 编著的《编
程拾贝》,p。81'15' 。
■异常:只有在不正常的情况下,才应放弃异常处理模块。什么才叫“不正常”呢?这通常是指程序遇到了
问题,而这一般是不愿见到的,所以性能不再成为优先考虑的目标。进行优化时,将小的“try…catch”块合
并到一起。由于这些块将代码分割成小的、各自独立的片断,所以会妨碍编译器进行优化。另一方面,若过
份热衷于删除异常处理模块,也可能造成代码健壮程度的下降。
■散列处理:首先,Java 1。0 和 1。1 的标准“散列表”(Hashtable)类需要造型以及特别消耗系统资源的
同步处理(570 单位的赋值时间)。其次,早期的 JDK 库不能自动决定最佳的表格尺寸。最后,散列函数应
针对实际使用项(Key)的特征设计。考虑到所有这些原因,我们可特别设计一个散列类,令其与特定的应用
程序配合,从而改善常规散列表的性能。注意 Java 1。2 集合库的散列映射(HashMap)具有更大的灵活性,
而且不会自动同步。
■方法内嵌:只有在方法属于final (最终)、private (专用)或static (静态)的情况下,Java 编译器
才能内嵌这个方法。而且某些情况下,还要求它绝对不可以有局部变量。若代码花大量时间调用一个不含上
述任何属性的方法,那么请考虑为其编写一个“final”版本。
■I/O:应尽可能使用缓冲。否则,最终也许就是一次仅输入/输出一个字节的恶果。注意 JDK 1。0 的I/O 类
采用了大量同步措施,所以若使用象readFully()这样的一个“大批量”调用,然后由自己解释数据,就可
获得更佳的性能。也要注意Java 1。1 的“reader”和“writer”类已针对性能进行了优化。
■造型和实例:造型会耗去2 到 200 个单位的赋值时间。开销更大的甚至要求上溯继承(遗传)结构。其他
681
…………………………………………………………Page 683……………………………………………………………
高代价的操作会损失和恢复更低层结构的能力。
■图形:利用剪切技术,减少在repaint()中的工作量;倍增缓冲区,提高接收速度;同时利用图形压缩技
术,缩短下载时间。来自JavaWorld 的“Java Applets”以及来自 Sun 的“Performing Animation”是两个
很好的教程。请记着使用最贴切的命令。例如,为根据一系列点画一个多边形,和drawLine()相比,
drawPolygon()的速度要快得多。如必须画一条单像素粗细的直线,drawLine(x;y;x;y)的速度比
fillRect(x;y;1;1)快。
■使用API 类:尽量使用来自 Java API 的类,因为它们本身已针对机器的性能进行了优化。这是用Java 难
于达到的。比如在复制任意长度的一个数组时,arraryCopy()比使用循环的速度快得多。
■替换API 类:有些时候,API 类提供了比我们希望更多的功能,相应的执行时间也会增加。因此,可定做
特别的版本,让它做更少的事情,但可更快地运行。例如,假定一个应用程序需要一个容器来保存大量数
组。为加快执行速度,可将原来的 Vector (矢量)替换成更快的动态对象数组。
1。 其他建议
■将重复的常数计算移至关键循环之外——比如计算固定长度缓冲区的buffer。length 。
■static final (静态最终)常数有助于编译器优化程序。
■实现固定长度的循环。
■使用javac 的优化选项:…O。它通过内嵌static,final 以及private 方法,从而优化编译过的代码。注
意类的长度可能会增加(只对JDK 1。1 而言——更早的版本也许不能执行字节查证)。新型的“Just…in
time”(JIT)编译器会动态加速代码。
■尽可能地将计数减至 0——这使用了一个特殊的JVM 字节码。
D。4 参考资源
D。4。1 性能工具
'1' 运行于 Pentium Pro 200,Netscape 3。0,JDK 1。1。4 的MicroBenchmark (参见下面的参考资源'5')
'2' Sun的Java 文档页——JDK Java 解释器主题:
http://java。sun。/products/JDK/tools/win32/java。html
'3' Vladimir Bulatov的HyperProf
http://physics。orst。edu/~bulatov/HyperProf
'4' Greg White 的ProfileViewer
http://inetmi。/~gwhi/ProfileViewer/ProfileViewer。html
D。4。2 Web 站点
'5' 对于Java 代码的优化主题,最出色的在线参考资源是Jonathan Hardwick 的“Java Optimization”网
站:
http://cs。cmu。edu/~jch/java/optimization。html
“Java 优化工具”主页:
http://cs。cmu。edu/~jch/java/tools。html
以及“Java Microbenchmarks”(有一个45 秒钟的评测过程):
http://cs。cmu。edu/~jch/java/benchmarks。html
D。4。3 文章
'6' “Make Java fast:Optimize! How to get the greatest performanceout of your code through low
level optimizations in Java”(让Java 更快:优化!如何通过在Java 中的低级优化,使代码发挥最出色
的性能)。作者:Doug Bell。网址:
http://javaworld。/javaworld/jw…04…1997/jw…04…optimize。html
(含一个全面的性能评测程序片,有详尽注释)
'7' “Java Optimization Resources ”(Java 优化资源)
http://cs。cmu。edu/~jch/java/resources。html
'8' “Optimizing Java for Speed”(优化Java,提高速度):
http://cs。cmu。edu/~jch/java/speed。html
682
…………………………………………………………Page 684……………………………………………………………
'9' “An Empirical Study of FORTRAN Programs”(FORTRAN 程序实战解析)。作者:Donald Knuth。
1971 年出版。第 1卷,p。105…33,“软件——实践和练习”。
'10' “Building High…Performance Applications and Servers in Java:An Experiential Study”。作
者:Jimmy Nguyen,Michael Fraenkel,RichardRedpath,Binh Q。 Nguyen 以及Sandeep K。 Singhal。IBM
T。J。 Watson ResearchCenter;IBM Software Solutions。
http://ibm。/java/education/javahipr。html
D。4。4 Java 专业书籍
'11' 《Advanced Java ,Idioms,Pitfalls ,Styles; and Programming Tips》。作者:Chris Laffra。
Prentice Hall 1997 年出版(Java 1。0 )。第 11 章第20 小节。
D。4。5 一般书籍
'12' 《Data Structures and C Programs》(数据结构和C 程序)。作者:J。Van Wyk。Addison…Wesly
1998 年出版。
'13' 《Writing Efficient Programs》(编写有效的程序)。作者:Jon Bentley。Prentice Hall 1982 年
出版。特别参考p。110 和p。145…151。
'14' 《More Programming Pearls》(编程拾贝第二版)。作者:JonBentley。“Association for
puting Machinery”,1998 年2 月。
'15' 《Programming Pearls 》(编程拾贝)。作者:Jone Bentley。Addison…Wesley 1989 年出版。第2 部
分强调了常规的性能改善问题。 '16' 《Code plete:A Practical Handbook of Software
Construction》(完整代码索引:实用软件开发手册)。作者:Steve McConnell。Microsoft 出版社 1993
年出版,第 9 章。
'17' 《Object…Oriented System Development》(面向对象系统的开发)。作者:Champeaux,Lea 和
Faure。第25 章。
'18' 《The Art of Programming》(编程艺术)。作者:Donald Knuth。第1卷“基本算法第3 版”;第3
卷“排序和搜索第 2 版”。Addison…Wesley 出版。这是有关程序算法的一本百科全书。
'19' 《Algorithms in C:Fundammentals;Data Structures; Sorting;Searching》(C 算法:基础、数据结
构、排序、搜索)第3 版。作者:RobertSedgewick。Addison…Wesley 1997 年出版。作者是Knuth 的学生。
这是专门讨论几种语言的七个版本之一。对算法进行了深入浅出的解释。
683
…………………………………………………………Page 685……………………………………………………………
附录 E 关于垃圾收集的一些话
“很难相信Java 居然能和C++一样快,甚至还能更快一些。”
据我自己的实践,这种说法确实成立。然而,我也发现许多关于速度的怀疑都来自一些早期的实现方式。由
于这些方式并非特别有效,所以没有一个模型可供参考,不能解释Java 速度快的原因。
我之所以想到速度,部分原因是由于C++模型。C++将自己的主要精力放在编译期间“静态”发生的所有事情
上,所以程序的运行期版本非常短小和快速。C++也直接建立在C 模型的基础上(主要为了向后兼容),但有
时仅仅由于它在C 中能按特定的方式工作,所以也是C++中最方便的一种方法。最重要的一种情况是C 和C++
对内存的管理方式,它是某些人觉得Java 速度肯定慢的重要依据:在Java 中,所有对象都必须在内存
“堆”里创建。
而在C++中,对象是在堆栈中创建的。这样可达到更快的速度,因为当我们进入一个特定的作用域时,堆栈
指针会向下移动一个单位,为那个作用域内创建的、以堆栈为基础的所有对象分配存储空间。而当我们离开
作用域的时候(调用完毕所有局部构建器后),堆栈指针会向上移动一个单位。然而,在C++里创建“内存
堆”(Heap )对象通常会慢得多,因为它建立在C 的内存堆基础上。这种内存堆实际是一个大的内存池,要
求必须进行再循环(再生)。在C++里调用 delete 以后,释放的内存会在堆里留下一个洞,所以再调用new
的时候,存储分配机制必须进行某种形式的搜索,使对象的存储与堆内任何现成的洞相配,否则就会很快用
光堆的存储空间。之所以内存堆的分配会在C++里对性能造成如此重大的性能影响,对可用内存的搜索正是
一个重要的原因。所以创建基于堆栈的对象要快得多。
同样地,由于C++如此多的工作都在编译期间进行,所以必须考虑这方面的因素。但在 Java 的某些地方,事
情的发生却要显得“动态”得多,它会改变模型。创建对象的时候,垃圾收集器的使用对于提高对象创建的
速度产生了显著的影响。从表面上看,这种说法似乎有些奇怪——存储空间的释放会对存储空间的分配造成
影响,但它正是JVM 采取的重要手段之一,这意味着在 Java 中为堆对象分配存储空间几乎能达到与C++中在
堆栈里创建存储空间一样快的速度。
可将C++的堆(以及更慢的 Java 堆)想象成一个庭院,每个对象都拥有自己的一块地皮。在以后的某个时
间,这种“不动产”会被抛弃,而且必须再生。但在某些 JVM 里,Java 堆的工作方式却是颇有不同的。它更
象一条传送带:每次分配了一个新对象后,都会朝前移动。这意味着对象存储空间的分配可以达到非常快的
速度。“堆指针”简单地向前移至处女地,所以它与C++的堆栈分配方式几乎是完全相同的(当然,在数据
记录上会多花一些开销,但要比搜索存储空间快多了)。
现在,大家可能注意到了堆事实并非一条传送带。如按那种方式对待它,最终就要求进行大量的页交换(这
对性能的发挥会产生巨大干扰),这样终究会用光内存,出现内存分页错误。所以这儿必须采取一个技巧,
那就是著名的“垃圾收集器”。它在收集“垃圾”的同时,也负责压缩堆里的所有对象,将“堆指针”移至
尽可能靠近传送带开头的地方,远离发生(内存)分页错误的地点。垃圾收集器会重新安排所有东西,使其
成为一个高速、无限自由的堆模型,同时游刃有余地分配存储空间。
为真正掌握它的工作原理,我们首先需要理解不同垃圾收集器(GC)采取的工作方案。一种简单、但速度较
慢的GC 技术是引用计数。这意味着每个对象都包含了一个引用计数器。每当一个句柄同一个对象连接起