《Java程序性能优化》读书笔记
很不错的书籍。值得反复阅读。附带下载地址。
第一章 java性能调优概述
一般来说,程序的性能的表现方面:
- 执行速度
- 内存分配
- 启动时间
- 负载承受能力
性能参考指标:
- 执行时间
- CPU时间
- 内存分配
- 磁盘吞吐量
- 网络吞吐量
- 响应时间
木桶原理与性能瓶颈,最有可能成为性能瓶颈的环节:
- 磁盘IO
- 网络操作
- CPU
- 异常
- 数据库
- 锁竞争
- 内存
加速比=优化前系统耗时/优化后系统耗时
Amdahl定律:
加速比speedup<=1/(F+(1-F)/N)
其中N为CPU处理器数量,F为系统内必须串行化的程序比重。由此可见,为了提高系统的运行速度,仅仅增加N,是不能提高运行速度的,从根本上修改程序的串行化行为,提高系统内并行模块的占比,才行。
性能调优层次:
- 设计调优
- 代码调优
- JVM调优
- 数据库调优:1. 应用层SQL优化;2.数据库优化;3.数据库软件优化;
- 操作系统调优:共享内存段、信号量、共享内存最大值shmmax、共享内存最小值shmmin、最大文件句柄数、虚拟内存大小、磁盘块大小。
第二章 设计优化
常用手段
- 善用设计模式
- 单例模式
- 代理模式
- 享元模式
- 装饰者模式
- 观察者模式
- value object模式
- 业务代理模式
- 常用的优化组件和方法
- 缓冲buffer,缓冲区是一块特定的内存区域;缓冲区不宜过大,浪费系统内存,增加GC负担;
- 缓存cache,WeakHashMap;EHCache数据缓存解决方案,OSCache,可用于缓存任何对象,JBossCache可用于JBoss集群间数据共享的缓存框架;EHCache的缺点是缓存组件和业务层代码紧密耦合,依赖性太强;基于动态代理的缓存;
- 对象复用-池:线程池,数据库连接池;适用场景:创建耗时的大对象,节省获取对象实例的成本,减少GC负担;生成实例成本小的对象,使用池的方式,得不偿失;JDK new操作的效率很高,但是new操作所调用的类的构造函数可能很耗时;apache commons-pool对象池组件,对象池接口ObjectPool,PoolableObjectFactory,内置三个对象池,StackObjectPool,GenericObjectPool,SoftReferenceObjectPool;
- 并行代替串行
- 负载均衡,Apache+Tomcat集群搭建负载均衡解决方案,Session共享模式,黏性Session模式和复制Session模式;跨JVM分布式缓存框架Terracotta,可以实现Tomcat的Session共享;
- 时间换空间,看系统的性能瓶颈是什么,空间是瓶颈,则采用时间换空间;
- 空间换时间
第三章 java程序优化
- 字符串优化处理
- 核心数据结构
- 使用NIO
- 引用类型
- 强引用
- 软引用
- 弱引用
- 虚引用
- 其他技巧
- 异常try…catch,慎用,循环中不能用;
- 使用局部变量;
- 位运算代替乘除法;
- 少用switch,尤其是分支多的情况;
- 一位数组代替二维数组,减少集合的方法调用;
- 提取表达式(重复的部分),尤其是遇到循环;
- 展开循环;
- 布尔运算代替位运算(条件判断&&||,断路原则);
- arrayCopy();
- 使用buffer进行IO操作;
- 使用clone()代替new;
- 静态方法代替实例方法;
String对象的3个特点:
- 不变性;
- 针对常量池的优化:当两个String对象拥有相同的值时,他们只是引用常量池的同一个拷贝;
- 类的final定义;
字符串分割与查找
第四章 并行程序开发优化
- 并行程序设计模式
- Future模式,即异步;
- master-worker模式,类似于fork-join,master进程负责接收和分配任务,worker进程负责处理子任务;
- Guarded Suspension模式,
- 不变模式,不变模式比只读属性具有更强的一致性和不变性,只读属性自身可能变化;不变模式的使用条件:1. 对象创建后,其内部状态和数据不再发生变化;2. 对象需要被共享,被多线程频繁访问。实现4要点:1. 去掉setter方法和所有修改自身属性的方法;2. 所有属性私有且final;3. 没有子类可以继承并重写其方法;4. 构造函数可以创建完整对象。
- 生产者-消费者模式;
- JDK多任务执行框架
- 无限制线程的缺陷
- 简单的线程池实现
- Executor框架
- 自定义线程池
- 优化线程池大小,估算公式:
N = Nc * Uc * (1+W/C)
,其中Nc是CPU的数量,Uc是CPU的使用率,W/C是等待时间与计算时间的比率。 - 扩展ThreadPoolExecutor
- JDK并发数据结构
- 并发List
- 并发Set
- 并发Map
- 并发Queue
- 并发Deque
- 并发控制方法
- 锁性能与优化
- 无锁并行计算
- 协程
第五章 JVM调优
JVM 内存模型
- 程序计数器
- java虚拟机栈
- 本地方法栈
- java堆
- 方法区
JVM内存分配参数
- 最大堆内存
- 最小堆内存
- 新生代
- 持久代
- 线程栈
- 堆的比例分配
垃圾收集
垃圾收集器要处理的基本问题:
- 哪些对象需要回收?
- 何时回收这些对象?
- 怎么回收这些对象?
算法:
- 引用计数法(reference counting):问题:循环引用;
- 标记-清除算法(mark-sweep),两个阶段,可达性分析;问题:空间碎片,尤其是大对象的内存分配,不连续的内存空间的工作效率要低于连续的空间。
- 复制(copy),代价:内存折半;其高效性建立在存活对象少、垃圾对象多的情况。
- 标记压缩(mark-compact),将所有的存活对象压缩到内存的另一侧;
- 增量(incremental collecting),垃圾回收时,stw,所有线程挂起;故,垃圾回收与应用程序交替执行;但是线程切换和上下文转换的消耗,使得GC的成本提高,吞吐量下降。
- 分代(generational collecting)
垃圾收集器的类型
按线程数分,串行GC和并行GC;
工作模式分,并发式和独占式;
碎片处理方式分,压缩和非压缩;
工作的内存区间分,新生代和老年代;
评价GC策略的指标:
- 吞吐量
- 垃圾回收器负载
- 停顿时间
- 垃圾回收频率
- 反应时间
- 堆分配
新生代串行收集器:单线程、独占式,stop the world,client模式下默认的GC,没有线程切换的开销;
老年代串行收集器:标记-压缩,单线程、独占式,CMS的备用GC;
并行收集器:多线程,独占式,有线程切换的开销,线程数通过-XX:ParallelGCThreads指定,CPU<8,设置为GC数,CPU>8,设置为3+[(5*CPU_count)/8]
第六章 性能调优工具
1. Linux命令行
1. top
2. sar
3. vmstat
4. iostat
5. pidstat
2. Windows工具
任务管理器
perfmon性能监控工具
process explorer
加强版的任务管理器
pslist
基本用法:
pslist [-d] [-m] [-x] [-t] [-s [n]] [-r n] [name | pid]
3. JDK命令行
1. jps
2. jstat
3. jmap
4. jstack
5. jinfo
6. jhat
7. jstatd
8. hprof