11_性能问题
性能问题
一、性能优化
1.1 目的
- 提高程序的整体运行性能
1.2 原则
- 始终把安全性放在第一
- 程序正确后,再想办法提高运行速度
1.3 指标
- 吞吐率优化:更有效地利用现有资源,用同样的资源做更多的事
- 可伸缩性优化:当增加新资源时,新资源可以得到利用,程序的性能可以提升
1.4 权衡因素
- 进行性能优化时,一定要有明确的性能需求,清楚了解什么时候优化,什么时候不优化
- 必须以测试为基准,验证性能的优化程度,不要靠猜测
- 做出性能优化决策时,必须考虑以下问题:
- 做了什么优化?优化了什么地方?
- 在什么样的场景下优化了?其他场景是否使用?
- 优化的场景发生概率如何?
- 这种优化会带来哪些问题?需要付出什么代价?例如增加开发风险或维护开销?
- 避免不成熟的优化,一定要预估优化风险,以及验证优化结果
二、线程开销
2.1 上下文切换
- 线程调度,会引起上下文切换
- 上下文切换,会涉及到应用程序、JVM、操作系统等多方面的切换开销
- 在 JVM、操作系统上消耗的时间越多,应用程序可用时间越少
- 上下文切换的开销与平台有关,在大多数处理器中,上下文切换开销相当于 5000 ~ 10000 个时钟周期
- 线程数量越多(竞争)、IO阻塞越多(挂起),上下文切换越频繁
2.2 内存同步
- 同步实现会使用到特殊的硬件指令,比如内存栅栏,它有刷新缓存使得缓存无效、停止执行管道、禁止指令重排序等功能
- 同步需要锁定共享内存总线,而所有处理器都共享这条总线,所以同步期间会影响其他线程的性能
2.3 阻塞
- 阻塞会导致线程挂起,从而产生上下文切换
- 阻塞挂起至少会引起2次上下文切换,一次是挂起,一次是恢复
三、并发锁优化
有3种方式可以降低锁的竞争程度:
- 减少锁的持有时间
- 降低锁的请求频率
- 使用带有协调机制的独占锁,这些机制允许更高的并发性
3.1 减少锁持有时间
- 缩小同步代码块范围
- 拆分过大的同步代码块成小的同步代码块
- 同步代码块不宜过小,需保证安全性、原子性
- 同步代码块不易过多,避免频繁加锁解锁
3.2 降低锁粒度
- 减少锁的请求频率,即请求锁数量少了,就能减少发生竞争的可能性
3.2.1 锁分解
- 对象中存在多个相互独立的状态
- 对象存在适中而不激烈的锁竞争
- 根据相互独立的状态,使用不同的锁
3.2.2 锁分段
- 锁分解的进一步扩展
- 对象存在激烈的锁竞争
- 根据不同的数据范围,使用不同的锁
3.2.3 避免热点域
- 被多个锁共享的状态变量
- 多个操作都涉及到的状态变量
- 需要多个锁才能访问的状态变量
- 避免热点域,因为它往往需要加多个锁,或者加全局锁才能操作
3.3 避免独占锁
- 并发容器
- 读写锁分离
- 不可变对象
- 原子变量