描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787111669432
作者是国家认证系统分析师、Oracle数据库认证专家(OCP),长期从事Java开发,对Java程序设计和JVM有深入研究。
感受Java开发中的大智慧,让你的Java程序更优美
资深程序员深度分享Java程序性能优化的宝贵经验
从软件设计、编码和JVM等维度阐述性能优化的方法和技巧
通过大量示例,全方位展现Java程序性能优化的技巧;
专注于Java程序性能优化的方法、技巧和思想,深度剖析JDK部分的实现;
深入剖析软件设计层面、代码层面、JVM虚拟机层面的优化方法;
理论结合实践,真正将性能优化的相关技巧应用到实践中。
Java是目前应用非常广泛的软件开发平台,学习针对Java程序的优化方法有重要的现实意义。本书以Java程序性能优化为主线,系统地阐述与其相关的知识点,帮助读者掌握编写高质量Java程序的技巧,让他们感受Java开发中的大智慧,编写出更加优美的程序。
《Java程序性能优化实战》共6章,从软件设计、软件编码、JVM调优及程序故障排除等方面介绍Java程序性能优化的方法。第1章介绍性能的基本概念、木桶原理、Amdahl定律,以及系统调优的步骤和注意事项;第2章从设计层面介绍与性能相关的设计模式及常用的优化组件;第3章从代码层面介绍如何编写高性能的Java程序;第4章介绍并行程序开发,以及如何通过多线程提高系统的性能;第5章立足于JVM 虚拟机层面,介绍如何通过设置合理的JVM参数提升Java程序的性能;第6章介绍获取和监控程序及系统性能指标的各种工具,包括相关的故障排查工具。
《Java程序性能优化实战》适合所有的Java程序员、软件设计师、架构师及软件开发爱好者阅读。对于有一定经验的Java工程师,本书能帮助他们突破技术瓶颈,提高开发水平。
前言
第1章 Java性能调优概述 1
1.1 性能概述 1
1.1.1 看懂程序的性能 1
1.1.2 性能的参考指标 2
1.1.3 木桶原理与性能瓶颈 2
1.1.4 Amdahl定律 3
1.2 性能调优的层次 5
1.2.1 设计调优 5
1.2.2 代码调优 6
1.2.3 JVM调优 6
1.2.4 数据库调优 6
1.2.5 操作系统调优 7
1.3 基本调优策略和手段 7
1.3.1 优化的一般步骤 8
1.3.2 系统优化的注意事项 8
1.4 小结 9
第2章 设计优化 10
2.1 善用设计模式 10
2.1.1 单例模式 10
2.1.2 代理模式 15
2.1.3 享元模式 24
2.1.4 装饰者模式 28
2.1.5 观察者模式 34
2.1.6 值对象模式 38
2.1.7 业务代理模式 41
2.2 常用的优化组件和方法 44
2.2.1 缓冲 44
2.2.2 缓存 47
2.2.3 对象复用——池 51
2.2.4 并行替代串行 58
2.2.5 负载均衡 58
2.2.6 时间换空间 63
2.2.7 空间换时间 64
2.3 小结 66
第3章 Java程序优化 67
3.1 字符串优化处理 67
3.1.1 String对象及其特点 67
3.1.2 substring()方法的内存泄漏 69
3.1.3 字符串分割和查找 72
3.1.4 StringBuffer和StringBuilder 76
3.1.5 CompactStrings优化字符串存储 80
3.2 核心数据结构 81
3.2.1 List接口 81
3.2.2 Map接口 88
3.2.3 Set接口 100
3.2.4 优化集合访问代码 101
3.2.5 RandomAccess接口 103
3.3 使用NIO提升性能 105
3.3.1 NIO中的Buffer类族和Channel 106
3.3.2 Buffer的基本原理 107
3.3.3 Buffer的相关操作 109
3.3.4 MappedByteBuffer性能评估 116
3.3.5 直接访问内存 119
3.4 引用类型 121
3.4.1 强引用 121
3.4.2 软引用 122
3.4.3 弱引用 123
3.4.4 虚引用 124
3.4.5 WeakHashMap类及其实现 127
3.5 性能测试工具JMH 129
3.5.1 JMH之Hello World 130
3.5.2 JMH之指定测量模式 131
3.5.3 JMH之对象作用域 134
3.5.4 JMH之代码消除 135
3.6 有助于改善性能的技巧 137
3.6.1 使用局部变量 137
3.6.2 位运算代替乘除法 138
3.6.3 替换switch 139
3.6.4 一维数组代替二维数组 141
3.6.5 提取表达式 142
3.6.6 展开循环 143
3.6.7 布尔运算代替位运算 144
3.6.8 使用arrayCopy() 145
3.6.9 使用Buffer进行I/O操作 147
3.6.10 使用clone()代替new 149
3.6.11 慎用Java函数式编程 151
3.7 小结 152
第4章 并行程序开发及优化 153
4.1 并行程序设计模式 153
4.1.1 Future模式 153
4.1.2 Master-Worker模式 161
4.1.3 Guarded Suspension模式 165
4.1.4 不变模式 172
4.1.5 生产者-消费者模式 174
4.2 JDK多任务执行框架 178
4.2.1 无限制线程的缺陷 178
4.2.2 简单的线程池实现 179
4.2.3 Executor框架 183
4.2.4 自定义线程池 185
4.2.5 优化线程池大小 189
4.2.6 扩展ThreadPoolExecutor 189
4.3 JDK并发数据结构 191
4.3.1 并发List 191
4.3.2 并发Set 193
4.3.3 并发Map 194
4.3.4 并发Queue 195
4.3.5 并发Deque 197
4.4 并发控制方法 199
4.4.1 Java内存模型与volatile 199
4.4.2 同步关键字synchronized 203
4.4.3 重入锁 205
4.4.4 读写锁 207
4.4.5 读写锁的改进:StampedLock 209
4.4.6 Condition对象 210
4.4.7 信号量 212
4.4.8 线程局部变量ThreadLocal 214
4.5 锁的性能和优化 215
4.5.1 线程的开销 215
4.5.2 避免死锁 215
4.5.3 减少锁持有时间 219
4.5.4 减小锁粒度 220
4.5.5 读写分离锁来替换独占锁 221
4.5.6 锁分离 222
4.5.7 重入锁和内部锁 224
4.5.8 锁粗化 224
4.5.9 自旋锁 226
4.5.10 锁消除 226
4.5.11 锁偏向 227
4.6 无锁的并行计算 228
4.6.1 非阻塞的同步/无锁 228
4.6.2 原子操作 228
4.6.3 Amino框架简介 231
4.6.4 Amino集合 231
4.6.5 Amino树 236
4.6.6 Amino图 237
4.6.7 Amino简单调度模式 238
4.7 协程 240
4.7.1 协程的概念 240
4.7.2 Kilim框架简介 241
4.7.3 Task及其状态 242
4.7.4 Fiber及其状态 242
4.7.5 Kilim开发环境配置 243
4.7.6 Kilim之Hello World 244
4.7.7 多任务通信 246
4.7.8 Kilim实例及性能评估 247
4.8 小结 250
第5章 JVM调优 251
5.1 Java虚拟机内存模型 251
5.1.1 程序计数器 251
5.1.2 Java虚拟机栈 252
5.1.3 本地方法栈 258
5.1.4 Java堆 258
5.1.5 方法区 260
5.2 JVM内存分配参数 263
5.2.1 设置最大堆内存 264
5.2.2 设置最小堆内存 264
5.2.3 设置新生代 266
5.2.4 设置持久代 266
5.2.5 设置线程栈 267
5.2.6 堆的比例分配 269
5.2.7 堆分配参数总结 270
5.3 垃圾收集基础 271
5.3.1 垃圾收集的作用 272
5.3.2 垃圾回收算法与思想 272
5.3.3 垃圾回收器的类型 277
5.3.4 评价GC策略的指标 278
5.3.5 新生代串行回收器 278
5.3.6 老年代串行回收器 279
5.3.7 并行回收器 280
5.3.8 新生代并行回收器 281
5.3.9 老年代并行回收器 282
5.3.10 CMS回收器 282
5.3.11 G1回收器 285
5.3.12 Stop the World案例 286
5.3.13 垃圾回收器对系统性能的影响 288
5.3.14 GC操作相关参数总结 289
5.4 常用调优案例和方法 291
5.4.1 将新对象预留在新生代 291
5.4.2 大对象进入老年代 294
5.4.3 设置对象进入老年代的年龄 296
5.4.4 稳定与振荡的堆大小 296
5.4.5 吞吐量优先案例 298
5.4.6 使用大页案例 298
5.4.7 降低停顿案例 299
5.5 实用JVM参数 299
5.5.1 JIT编译参数 299
5.5.2 堆快照 301
5.5.3 错误处理 302
5.5.4 获取GC信息 302
5.5.5 类和对象跟踪 304
5.5.6 控制GC 305
5.5.7 选择类校验器 305
5.5.8 Solaris下的线程控制 306
5.5.9 使用大页 306
5.5.10 压缩指针 306
5.6 JVM调优实战 306
5.6.1 Tomcat简介与启动加速 307
5.6.2 Web应用程序简介 309
5.6.3 JMeter简介与使用 310
5.6.4 调优前Web应用运行状况 313
5.6.5 调优过程 314
5.7 小结 315
第6章 Java性能调优工具 316
6.1 Linux命令行工具 316
6.1.1 top命令 316
6.1.2 sar命令 318
6.1.3 vmstat命令 319
6.1.4 iostat命令 321
6.1.5 pidstat工具 322
6.2 Windows工具 326
6.2.1 任务管理器 326
6.2.2 perfmon性能监控工具 328
6.2.3 Process Explorer工具 331
6.2.4 pslist命令行 333
6.3 JDK命令行工具 334
6.3.1 jps命令 335
6.3.2 jstat命令 336
6.3.3 jinfo命令 339
6.3.4 jmap命令 340
6.3.5 jhat命令 341
6.3.6 jstack命令 343
6.3.7 jstatd命令 346
6.3.8 hprof工具 347
6.3.9 jcmd命令 349
6.4 JConsole工具 350
6.4.1 JConsole连接Java程序 350
6.4.2 Java程序概况 351
6.4.3 内存监控 352
6.4.4 线程监控 352
6.4.5 类加载情况 354
6.4.6 虚拟机信息 354
6.4.7 MBean管理 355
6.4.8 使用插件 356
6.5 Visual VM多合一工具 357
6.5.1 Visual VM连接应用程序 358
6.5.2 监控应用程序概况 359
6.5.3 Thread Dump和分析 361
6.5.4 性能分析 362
6.5.5 快照 365
6.5.6 内存快照分析 365
6.5.7 MBean管理功能 367
6.5.8 TDA的使用 367
6.5.9 BTrace简介 368
6.6 Visual VM对OQL的支持 374
6.6.1 Visual VM的OQL基本语法 374
6.6.2 内置heap对象 375
6.6.3 对象函数 376
6.6.4 集合/统计函数 380
6.6.5 程序化OQL 384
6.7 MAT内存分析工具 386
6.7.1 初识MAT 386
6.7.2 浅堆和深堆 389
6.7.3 支配树 392
6.7.4 垃圾回收根 394
6.7.5 内存泄漏检测 395
6.7.6 最大对象报告 396
6.7.7 查找支配者 396
6.7.8 线程分析 397
6.7.9 集合使用情况分析 398
6.7.10 扩展MAT 399
6.8 MAT对OQL的支持 403
6.8.1 Select子句 404
6.8.2 From子句 406
6.8.3 Where子句 407
6.8.4 内置对象与方法 408
6.9 来自JRockit的礼物——JMC 411
6.9.1 得到JFR文件 412
6.9.2 Java程序的整体运行情况 413
6.9.3 CPU分析 413
6.9.4 内存分析 414
6.9.5 I/O分析 416
6.10 小结 418
Java是目前应用最为广泛的软件开发平台之一。随着Java及Java社区的不断壮大,Java早已不再是一门简单的计算机语言了,它更是一个平台、一种文化、一个社区。
作为一个平台,JVM虚拟机起着举足轻重的作用。除了Java语言,任何一种能够被编译成字节码的计算机语言都属于Java这个平台。Groovy、Scala和JRuby等都是Java平台的一部分,它们依赖于JVM虚拟机,同时,Java平台也因为它们而变得更加丰富多彩。
作为一种文化,Java几乎成为“开源”的代名词。在Java平台上,有大量的开源软件和框架,如Tomcat、Struts、Hibernate和Spring等。就连JDK和JVM自身也有不少的开源实现版本,如OpenJDK和Harmony。可以说,“共享”的精神在Java世界里体现得淋漓尽致。
作为一个社区,Java拥有无数的开发人员、数不清的论坛和资料。从桌面应用软件和嵌入式开发,到企业级应用、后台服务器和中间件,都可以看到Java的身影。其应用形式之复杂、参与人数之庞大也令人咂舌。可以说,Java社区已经成为一个良好而庞大的生态系统。
《Java程序性能优化实战》特色
《Java程序性能优化实战》的主要特色有:
专注于Java应用程序的优化方法、技巧和思想,并深度剖析JDK部分的实现。
具有较强的层次性和连贯性,深入剖析软件设计层面、代码层面和JVM虚拟机层面的优化方法。
理论结合实践,使用丰富的示例帮助读者理解理论知识。
《Java程序性能优化实战》内容
《Java程序性能优化实战》主要介绍Java应用程序的优化方法和技巧,共分为6章。
第1章介绍性能的基本概念、两个重要理论(木桶原理和Amdahl定律),以及系统调优的一般步骤与注意事项。
第2章从设计层面介绍与性能相关的设计模式、组件及有助于改善性能的软件设计思想。
第3章从代码层面介绍如何编写高性能的Java代码,涉及的主要内容有字符串的优化处理、文件I/O的优化、核心数据结构的使用、Java的引用类型及一些常用的惯例。
第4章介绍并行程序开发的相关知识,以及如何通过多线程提高系统性能,涉及的主要内容有并发设计模式、多任务执行框架、并发数据结构的使用、并发控制方法、“锁”的优化、无锁的并行计算及协程。
第5章立足于JVM虚拟机层面,介绍如何通过设置合理的JVM参数提升Java程序的性能。
第6章主要介绍获取和监控程序或系统性能指标的各种工具,以及Java应用程序相关的故障排查工具。
读者对象
想要通读本书并取得良好的学习效果,读者需要具备Java的基础知识。本书不是一本帮助Java初学者入门的书籍,而是一本介绍如何编写高质量Java程序的书籍,主要适合以下读者阅读:
拥有一定开发经验的Java开发人员;
Java软件设计师和架构师;
系统调优人员;
有一定Java语言基础而想进一步提高开发水平的程序员。
《Java程序性能优化实战》约定
《Java程序性能优化实战》在讲解的过程中有如下约定:
书中所述的JDK 1.5、JDK 1.6、JDK 1.7和JDK 1.8,分别等同于JDK 5、JDK 6、JDK 7和JDK 8。
如无特殊说明,JVM虚拟机均指Hot Spot虚拟机。
如无特殊说明,《Java程序性能优化实战》中的程序和示例均可在JDK 1.6、JDK 1.7和JDK 1.8环境中运行。
配书资源获取方式
《Java程序性能优化实战》涉及的所有源代码需要读者自行下载。请在华章公司的网站(www.hzbook.com)上搜索到本书,然后单击“资料下载”按钮,即可在本书页面上找到下载链接。
致谢
在《Java程序性能优化实战》的写作过程中,我充满着感激之情。首先感谢我的家人!在本书完稿前,父亲病重,但由于我的工作繁忙,未能抽出太多时间照顾他,幸好得到了母亲的大力支持和父亲的谅解,我才能够全身心投入到写作之中。同时,母亲对我的悉心照料也让我能够更加专注于写作。
其次,感谢我的工作单位UT斯达康对我的支持和理解,让我能安心写作。另外,还要感谢两位前辈Rex Zhu和Tao Tao!正是他们在工作中对我的悉心指导,才能让我有所进步,而这一切,正是写作本书的基础。
最后,再次感谢我的母亲,祝她身体健康!
售后支持
Java程序性能优化涉及的知识较为庞杂,而且Java技术也在不断地迭代和发展,加之笔者水平和成书时间所限,书中可能存在一些疏漏和不当之处,敬请各位读者指正。阅读本书时若有疑问,请发电子邮件到[email protected]。
……
目前,市面上为数不多的讲解Java程序性能优化的图书内容不够深入,讲解也不够浅显易懂,有的甚至晦涩难懂。而本书不存在这些问题。本书结合作者多年的Java开发经验和对Java系统的深入理解,由浅入深地阐述Java程序性能优化的方法、技巧和思想,并通过大量示例展现各种参数带来的优化效果,从而帮助读者透彻地理解Java程序性能优化的要点,值得每一个Java程序员细读。
——中国联通济南软件研究院 郝军
Java程序性能优化虽然是一个老生常谈的话题,但想要把这个话题真正讲解清楚却并不容易。本书让人眼前一亮,很多困扰Java程序员的问题都能在书中找到答案。本书对软件设计优化、编码优化及JVM虚拟机优化均做了深入讲解,基本涵盖了Java程序性能优化的重要知识点,推荐想提升Java程序开发水平的程序员阅读。
——财神软件架构师 张蓉
本书总结了葛老师多年从事Java开发及培训的大量经验,并给出了他对软件架构、设计和优化等方面的建议,是一本凝聚了大量心血的诚意之作。本书通过大量示例深入剖析Java底层设计和架构,并对Java程序性能优化的技巧做了重点阐述,可以帮助读者提高Java开发水平,从而编写出更加健壮和优美的程序。
——本书的*个读者 靳华
第1章 Java性能调优概述
本章将对性能优化技术进行整体性概述,让读者了解性能的概念和性能优化的基本思路和方法。掌握这些内容,有助于读者对性能问题进行系统分析。
本章涉及的主要知识点有:
? 评价性能的主要指标;
? 木桶原理的概念及其在性能优化中的应用;
? Amdahl定律的含义;
? 性能调优的层次;
? 系统优化的一般步骤和注意事项。
1.1 性 能 概 述
为什么程序总是那么慢?它现在到底在干什么?时间都耗费在哪里了?也许,你经常会抱怨这些问题。如果是这样,那么说明你的程序出现了性能问题。和功能性问题相比,性能问题在有些情况下可能并不算什么太大的问题,将就将就,也就过去了。但是,严重的性能问题会导致程序瘫痪、假死,直至崩溃。本节就先来认识性能的各种表现和指标。
1.1.1 看懂程序的性能
对于客户端程序而言,拙劣的性能会严重影响用户体验。界面停顿、抖动、响应迟钝等问题会遭到用户不停地抱怨。一个典型的例子就是Eclipse IDE工具在执行Full GC时会出现程序“假死”现象,相信这一定被不少开发人员所诟病。对于服务器程序来说,性能问题则更为重要。相信不少后台服务器软件都有各自的性能目标,以Web服务器为例,服务器的响应时间和吞吐量就是两个重要的性能参数。当服务器承受巨大的访问压力时,可能出现响应时间变长、吞吐量下降,甚至抛出内存溢出异常而崩溃等问题。这些问题,都是性能调优需要解决的。
一般来说,程序的性能可以有以下几个方面的表现。
? 执行速度:程序的反应是否迅速,响应时间是否足够短。
? 内存分配:内存分配是否合理,是否过多地消耗内存或者存在泄漏。
? 启动时间:程序从运行到可以正常处理业务需要花费多长时间。
? 负载承受能力:当系统压力上升时,系统的执行速度和响应时间的上升曲线是否平缓。
1.1.2 性能的参考指标
为了能够科学地进行性能分析,对性能指标进行定量评测是非常重要的。目前,可以用于定量评测的性能指标有:
? 执行时间:一段代码从开始运行到运行结束所使用的时间。
? CPU时间:函数或者线程占用CPU的时间。
? 内存分配:程序在运行时占用的内存空间。
? 磁盘吞吐量:描述I/O的使用情况。
? 网络吞吐量:描述网络的使用情况。
? 响应时间:系统对某用户行为或者事件做出响应的时间。响应时间越短,性能越好。
1.1.3 木桶原理与性能瓶颈
木桶原理又称短板理论,其核心思想是一只木桶盛水的多少,并不取决于桶壁上最高的那块木板,而是取决于桶壁上最短的那块木板,如图1.1所示。
将这个理论应用到系统性能优化上可以这么理解,即使系统拥有充足的内存资源和CPU资源,但是,如果磁盘I/O性能低下,那么系统的总体性能是取决于当前最慢的磁盘I/O速度,而不是当前最优越的CPU或者内存。在这种情况下,如果需要进一步提升系统性能,优化内存或者CPU资源是毫无用处的,只有提高磁盘I/O性能才能对系统的整体性能进行优化。此时,磁盘I/O就是系统的性能瓶颈。
?注意:根据木桶原理,系统的最终性能取决于系统中性能表现最差的组件。因此,为了提升系统的整体性能,必须对系统中表现最差的组件进行优化,而不是对系统中表现良好的组件进行优化。
根据应用的特点不同,任何计算机资源都有可能成为系统瓶颈。其中,最有可能成为系统瓶颈的计算资源有:
? 磁盘I/O:由于磁盘I/O读写的速度比内存慢很多,程序在运行过程中,如果需要等待磁盘I/O完成,那么低效的I/O操作会拖累整个系统。
? 网络操作:对网络数据进行读写的情况与磁盘I/O类似,由于网络环境的不确定性,尤其是对互联网上数据的读写,网络操作的速度可能比本地磁盘I/O更慢,因此,如不加特殊处理,也极可能成为系统瓶颈。
? CPU:对计算资源要求较高的应用,由于其长时间、不间断地大量占用CPU资源,那么对CPU的争夺将导致性能问题。例如,科学计算、3D渲染等对CPU需求量大的应用便是如此。
? 异常:对Java应用来说,异常的捕获和处理是非常消耗资源的。如果程序高频率地进行异常处理,则整体性能便会有明显下降。
? 数据库:大部分应用程序都离不开数据库,而海量数据的读写操作往往是相当费时的。应用程序需要等待数据库操作完成并返回结果,那么缓慢的同步操作将成为系统瓶颈。
? 锁竞争:对高并发程序来说,如果存在激烈的锁竞争,对性能无疑是极大的打击。锁竞争将会明显增加线程上下文切换的开销,而且这些开销都是与应用需求无关的系统开销,白白占用宝贵的CPU资源,却不带来任何好处。
? 内存:一般来说,只要应用程序设计合理,内存在读写速度上不太可能成为性能瓶颈。除非应用程序进行了高频率的内存交换和扫描,但这种情况比较少见。内存制约系统性能最有可能出现的情况是内存容量不足。与磁盘相比,内存的容量似乎小得可怜,这意味着应用软件只能尽可能将常用的核心数据读入内存,这在一定程度上降低了系统性能。
1.1.4 Amdahl定律
Amdahl定律是计算机科学中非常重要的定律,它定义了串行系统并行化后的加速比的计算公式和理论上限。
加速比定义如下:
加速比=优化前系统耗时/优化后系统耗时
即所谓的加速比,就是优化前系统耗时与优化后系统耗时的比值。加速比越高,表明优化效果越明显。
Amdahl定律给出了加速比与系统并行度和处理器数量的关系。设加速比为Speedup,系统内必须串行化的程序比重为F,CPU处理器的数量为N,则有
根据这个公式可知,如果CPU处理器的数量趋于无穷,那么加速比与系统的串行化率成反比,如果系统中必须有50%的代码串行执行,那么系统的最大加速比为2。
假设有一程序分为5个步骤执行,每个执行步骤花费100个时间单位。其中,只有步骤2和步骤5可以进行并行,步骤1、3、4必须串行,如图1.2所示。在全串行的情况下,系统合计耗时500个时间单位。
图1.2 串行工作流程
若将步骤2和步骤5并行化,假设在双核处理器上,则有如图1.3所示的处理流程。在这种情况下,步骤2和步骤5的耗时将为50个时间单位,故系统整体耗时为400个时间单位。根据加速比的定义有:
加速比=优化前系统耗时/优化后系统耗时=500/400=1.25
或者使用Amdahl定律给出的加速比公式。由于5个步骤中,3个步骤必须串行,因此其串行化比重为3/5=0.6,即F=0.6,且双核处理器的处理器个数N为2。代入公式得:
加速比=1/(0.6 (1-0.6)/2)=1.25
图1.3 双核处理器上的并行化
在极端情况下,假设并行处理器个数为无穷大,则有如图1.4所示的处理过程。步骤2和步骤5的处理时间趋于0。即使这样,系统整体耗时依然大于300个时间单位,即加速比的极限为500/300=1.67。
使用加速比计算公式,N趋于无穷大,有Speedup=1/F,且F=0.6,故有Speedup=1.67。
图1.4 极端情况下的并行化
由此可见,为了提高系统的速度,仅增加CPU处理器的数量并不一定能起到有效的作用。需要从根本上修改程序的串行行为,提高系统内可并行化的模块比重,在此基础上合理增加并行处理器的数量,才能以最小的投入得到最大的加速比。
?注意:根据Amdahl定律,使用多核CPU对系统进行优化,优化的效果取决于CPU的数量及系统中串行化程序的比重。CPU数量越多,串行化比重越低,则优化效果越好。仅提高CPU数量而不降低程序的串行化比重,则无法提高系统性能。
1.2 性能调优的层次
为了提升系统性能,开发人员可以从系统的各个角度和层次对系统进行优化。除了最常见的代码优化外,在软件架构、JVM虚拟机、数据库及操作系统几个层面可以通过各种手段进行调优,从而在整体上提升系统的性能。
1.2.1 设计调优
设计调优处于所有调优手段的上层,它往往需要在软件开发之前进行。在软件开发之初,软件架构师就应该评估系统可能存在的各种潜在问题,并给出合理的设计方案。由于软件设计和架构对软件整体质量有决定性的影响,所以,设计调优对系统性能的影响也是最大的。如果说代码优化、JVM优化都是对系统在微观层面上“量”的优化,那么设计优化就是对系统在宏观层面上“质”的优化。
设计优化的一大显著特点是,它可以规避某一个组件的性能问题,而非改良该组件的实现。例如,系统中组件A需要等待某事件E才能触发一个行为。如果组件A通过循环监控不断监测事件E是否发生,其监测行为必然会占用部分系统资源,因此,开发人员必须在监测频率和资源消耗间取得平衡。如果监测频率太低,虽然减少了资源消耗,但是系统实时反应性就会降低。如果进行代码层的调优,就需要优化监测方法的实现以及求得一个最为恰当的监测频率。
而若将此问题预留在设计层解决,便可以使用事件通知的方式将系统行为进行倒置。例如,使用将在第2章中提到的观察者模式,在事件E发生的时刻,由事件E通知组件A,从而触发组件A的行为。这种设计方法弃用了存在性能隐患的循环监控,从根本上解决了这一问题。
从某种程度上说,设计优化直接决定了系统的整体品质。如果在设计层考虑不周,留下太多问题隐患,那么这些“质”上的问题,也许无法再通过代码层的优化进行弥补。因此,开发人员必须在软件设计之初,要认真、仔细地考虑软件系统的性能问题。
进行设计优化时,设计人员必须熟悉常用的软件设计方法、设计模式、基本性能组件和常用的优化思想,并将其有机地集成在软件系统中。
?注意:一个良好的系统设计可以规避很多潜在的性能问题。因此,尽可能多花一些时间在系统设计上,这是创建高性能程序的关键。
1.2.2 代码调优
代码调优是在软件开发过程中或者在软件开发完成后,软件维护过程中进行的对程序代码的改进和优化。代码优化涉及诸多编码技巧,需要开发人员熟悉相关语言的API,并在合适的场景中正确使用相关API或类库。同时,对算法和数据结构的灵活使用,也是代码优化的重要内容。
虽然代码优化是从微观上对性能进行调整,但是一个“好”的实现和一个“坏”的实现对系统的影响也是非常大的。例如,同样作为List的实现,LinkedList和ArrayList在随机访问上的性能却相差几个数量级。又如,同样是文件读写的实现,使用Stream方式与Java NIO方式,其性能可能又会相差一个数量级。
因此,与设计优化相比,虽然笔者将代码优化称为在微观层面上的优化,但它却是对系统性能产生最直接影响的优化方法。
1.2.3 JVM调优
由于Java软件总是运行在JVM虚拟机之上,因此对JVM虚拟机进行优化也能在一定程度上提升Java程序的性能。JVM调优通常可以在软件开发后期进行,如在软件开发完成或者在软件开发的某一里程碑阶段进行。
作为Java软件的运行平台,JVM的各项参数如JVM的堆大小和垃圾回收策略等,将会直接影响Java程序的性能。
要进行JVM层面的调优,需要开发人员对JVM的运行原理和基本内存结构有一定了解,例如堆内存的结构、GC的种类等,然后依据应用程序的特点,设置合理的JVM启动参数。
1.2.4 数据库调优
对绝大部分应用系统而言,数据库是必不可少的一部分。Java程序可以使用JDBC的方式连接数据库。对数据库的调优可以分为以下3个部分:
? 在应用层对SQL语句进行优化;
? 对数据库进行优化;
? 对数据库软件进行优化。
在应用层优化数据库访问,涉及大量的编程技巧。例如,当使用JDBC进行查询时,对于大量拥有相同结构的SQL查询,可以使用PreparedStatement代替Statement,提高数据库的查询效率;在Select语句中,显示指定要查询的列名,避免使用星号“*”。
在对数据库进行优化时,主要目的是建立一个具有良好表结构的数据库。例如,为了提高多表级联查询效率,可以合理地使用冗余字段;对于大表,可以使用行的水平切割或者类似于Oracle分区表的技术;为了提高数据库的查询效率,可以建立有效且合理的索引。
对于数据库软件的优化,根据不同的数据库,如Oracle、MySQL或SQL Server,都拥有不同的方式。以Oracle为例,设置合理大小的共享池、缓存缓冲区或者PGA,对Oracle的运行性能都有很大的影响。
鉴于本书的讨论范围,数据库优化将不作为本书的阐述重点。
1.2.5 操作系统调优
作为软件运行的基础平台,操作系统的性能对应用系统也有较大的影响。不同类型的操作系统,调优的手段和参数可能会有所不同。例如,在主流UNIX系统中,共享内存段、信号量、共享内存最大值(shmmax)、共享内存最小值(shmmin)等都是可以进行优化的系统资源。此外,如最大文件句柄数、虚拟内存大小、磁盘的块大小等参数都可能对软件的性能产生影响。图1.5展示了在Windows平台上配置虚拟内存的界面。
?说明:操作系统的性能调优不在本书的讨论范围内,有兴趣的读者可以参考相关书籍。
1.3 基本调优策略和手段
存在性能问题的系统,十有八九是由某一系统瓶颈导致的。只要找到该性能瓶颈,分析瓶颈的形成原因,对症下药,使用合理的方法解决系统瓶颈,就能从根本上提升性能。所以,系统性能优化的最主要目的就是查找并解决性能瓶颈问题。但值得注意的是,性能优化往往会涉及对原有的实现进行较大的修改,因此很难保证这些修改不引发新的问题。所以,在性能优化前,需要对性能优化的目标和使用的方法进行统筹安排。
1.3.1 优化的一般步骤
对软件系统进行优化,首先需要有明确的性能目标,清楚地指出优化的对象和最终目的。其次,需要在目标平台上对软件进行测试,通过各种性能监控和统计工具,观测和确认当前系统是否已经达到相关目标,若已经达到,则没有必要再进行优化;若尚未达到优化目标,则需要查找当前的性能瓶颈。
可能成为性能瓶颈的因素很多,如磁盘I/O、网络I/O和CPU。当找到性能瓶颈后,首先需要定位相关代码,确认是否在软件实现上存在问题或者具有优化的空间。若存在优化空间,则进行代码优化;否则需要考虑进行JVM层、数据库层或者操作系统的优化,甚至可以考虑修改原有设计,或者提升硬件性能。
当优化完成后,需要在目标平台上进行确认测试。若达到性能目标,则优化过程结束;否则需要再次查找系统瓶颈,如此反复,如图1.6所示。
图1.6 性能优化的一般步骤
1.3.2 系统优化的注意事项
性能优化虽然能提升软件的性能,但是优化过程往往伴随着一些风险和弊端。例如,为了优化某一段代码的实现,就需要重写原有的算法,而这就很可能引入新的Bug。重新实现新的功能模块同时也意味着需要重新对其进行完整的功能性测试,使优化前所做的测试工作变得毫无意义。而且,优化后的代码与优化前的代码相比,可能会比较晦涩难懂,在一定程度上影响了系统的可维护性。因此,软件优化需要在软件功能、正确性和可维护性之间取得平衡,而不应该过分地追求软件性能。
在进行优化前,必须要有明确的已知问题和性能目标,决不可为了“优化”而“优化”。因此在动手前,必须知道自己要干什么。任何优化都是为了解决具体的软件问题,如果软件已经可以正常工作,在没有性能问题暴露前,只是凭着主观臆断对某些模块进行性能改进,从软件规范化开发的角度来说是非常冒险的。因为修改后的新代码没有经过完整的测试,软件质量就没有保障。而且,优化后的性能提升幅度可能并不足以让开发者值得如此费尽心机。因此,在进行软件优化时,必须要进行慎重的评估。
?注意:性能调优必须有明确的目标,不要为了调优而调优。如果当前程序并没有明显的性能问题,盲目地进行调优,其风险可能远远大于收益。
1.4 小 结
通过本章的学习,读者应该了解性能的基本概念及常用的参考指标。此外,本章还较为详细地介绍了与性能调优相关的两个重要理论——木桶原理和Amdahl定律。
根据木桶原理,系统的最终性能总是由系统中性能最差的组件决定的,因此,改善该组件的性能对提升系统整体性能有重要的作用。而根据Amdahl定律可以知道,只是增加处理器数量对提升系统性能并没有太大的实际意义,还必须同时提高程序的并行化比重。
本章还简要介绍了在软件开发和维护过程中可以进行性能优化的各个阶段。例如,在软件的设计阶段,需要选用合理的软件结构和性能组件;在编码阶段,需要提高代码的执行效率;对于Java应用程序,在系统的运行期,还需要设置合理的JVM虚拟机参数;同时,优化数据库和操作系统也对系统整体性能有直接影响。
在本章的最后还简要介绍了性能优化的一般步骤和注意事项。
评论
还没有评论。