More Related Content
Similar to Java性能调优浅谈 (20)
Java性能调优浅谈
- 3. 性能测量指标 - 1
• 各类资源消耗情况:
磁盘IO
内存占用
CPU
数据库
网络
锁竞争
…
3
- 5. 性能测量指标 - 3
• 其他最常用的两个指标(OLTP vs. OLAP)
吞吐量
延迟(或响应时间)
• 低延迟不一定意味着高吞吐量
从山西到广州运煤,一列火车100小时(包括往返)可以运输
10000t 煤,而一架飞机20小时(包括往返)可以运输100t煤。
• 并发度 = 吞吐量 * 延迟
比如一个系统处理某种任务的延迟为1ms,吞吐量为 1000tps,
那么并发度就为1/1000 * 1000 = 1,即单线程模型。
5
- 10. 性能优化模式 – 代理模式2
• 动态代理模式不同实现性能比较
ASM方式实现的动态代理函数调用性能最高,但需要
对字节码很熟悉,并且不利于维护
JDK本身动态代理函数调用性能最差,而且不够灵活
JDK本身动态代理对象创建性能最高,因为使用native的
defineClass()方法
Java Assist和CGLib实现的动态代理函数调用性能优于
JDK动态代理,一般推荐用这两种实现动态代理
10
- 11. 性能优化模式 – 代理模式3
• 动态代理模式的实际应用
Hibernate使用CGLib动态代理实现,达到延迟加载对象
的属性(延迟访问数据库)目的:HibernateProxy接口
CGLib的net.sf.cglib.proxy.LazyLoader 接口可以方便地用
于实现对象延迟加载
11
- 13. 性能优化模式 – Value Object模式
• RMI调用中将多个请求零散信息的请求合并
成只请求一个Value Object
比如请求订单信息场景,将getOrderId、getOrderNum等合
并成getOrder。Order就是Value Object。
13
- 14. 性能优化模式 – 缓冲1
• 缓冲可以协调上层组件和下层组件的性能差,当上层组件
性能优于下层组件时,可以有效减少上层组件对下层组件
的等待时间
14
- 16. 性能优化模式 – 缓冲3
• Java IO中读写文件缓冲,有无缓冲性能差为18倍(积分系
统中同样写一个77MB的文件,无缓冲18s,有缓冲1s)
16
- 18. 性能优化模式 – 缓存
• 本地缓存
• 分布式缓存
Redis、Memcached
避免缓存穿透,比如有大量的null请求穿透Cache,则
考虑为null也作Cache
• 程序的时间局部性原理
18
- 19. 性能优化模式 – 池化
• 如果一个类的实例对象被频繁使用,那么不必每次都生成
新实例,可以将这些类的实例保存在一个池中,可以大大
减轻创建对象和销毁对象开销
数据库连接池
线程池
对象池
19
- 22. 性能优化模式 – 并行化
• 多进程
• 多线程:
线程池最佳线程数目 =(线程等待时间与线程CPU时间
之比 + 1)* CPU数目
• 实例 :
从磁盘串行和并行读取 30 张大小为 256K 的缩略图,
磁盘IO速度为 30 MB/s,磁盘每次寻道耗时 10 ms
22
- 24. 代码调优 – 字符串 1
• JDK 1.6的substring方法存在内存泄露风险,
JDK 1.7已修复
24
- 25. 代码调优 – 字符串2
• 字符串分割:优先使用StringTokenizer而不是String
类的split方法
• 在性能敏感代码中优先使用String类的charAt和
indexOf方法
比如用charAt代替startWith和endWith
• 涉及大量字符串拼接优先使用StringBuffer和
StringBuilder
• 如果能预估字符串长度,最好提供容量参数
25
- 26. 代码调优 – 数据结构1
• ArrayList和LinkedList的增加元素方法比较
add(E e):在末尾追加元素,ArrayList未必比LinkedList慢,如果ArrayList指
定了合适的capacity(capcity设置过小会频繁扩容)
add(int index, E e):在指定位置插入元素,LinkedList性能优势开始凸显
• ArrayList和LinkedList的删除元素方法,毋庸置疑LinkedList性能更
优
• ArrayList和LinkedList的遍历操作
不推荐使用for-each遍历
ArrayList推荐用for循环遍历(随机访问)
LinkedList推荐用迭代器遍历,避免用for循环遍历,速度奇慢
26
- 27. 代码调优 – 数据结构2
• HashMap和HashSet
哈希算法是否高效是性能的关键
良好的哈希算法除了函数本身运行性能高效外,还必
须满足尽量均匀地将值分散到哈希地址空间,并且哈
希冲突尽可能少
27
- 28. 代码调优 – 异常开销
• 异常很方便,开销也很大
主要是Throwable的构造函数调用了fillInStackTrace()
方法构建异常栈,如果异常层次一深开销就更大了
避免不必要的异常(如异常被用作正常业务流程控
制)
避免在循环体内使用try-catch
重写fillInStackTrace()方法,提供空实现
28
- 29. 代码调优 – IO与NIO
• 标准IO包中的类不足以满足高效读写文件的目的,只有
BufferedReader、BufferedWriter、BufferedInputStream、
BuffredOutputStream四个类提供缓冲读写支持
• NIO包极大改善了这个局面,Buffer是NIO的一个核心类
• 如果涉及大文件随机读写,建议使用MappedByteBuffer和
RandomAccessFile
29
- 32. 代码调优 – Master-Worker模式
• 包含一个Master线程和多个Worker线程,Master线程接收用户请求、协调
Worker线程并整合最终处理结果。多个Worker线程协作处理用户请求。JDK 7
开始提供了fork/join框架。
32
- 33. 代码调优 – 不变模式
• 使用不变模式后,不变类的所有实例方法都不需要同步
• JDK中的典型不变类String、Integer、Boolean等
• 不变类对象创建后,其内部状态不再发生变化
• 不变类必须保证下面4条
去除所有setter方法和所有修改内部状态方法
所有属性都用private final标记
确保没有子类可以重载修改它的行为
有一个可以创建完整对象的构造函数
33
- 34. 代码调优 – 并发数据结构
• CopyOnWriteArrayList
读不加锁,写时先获取对象副本,再修改副本最后写回,写会加
锁
适合读远多于写或者遍历操作数量远多于可变操作数量的场景,
比如监听器列表
• CopyOnWriteSet
• ConcurrentHashMap
• ConcurrentLinkedQueue
适用于高并发场景下的队列,无锁
• BlockingQueue
• …
34
- 35. 代码调优 – 锁优化
• 读写锁分离,ReadWriteLock
• 无锁化,ThreadLocal、CopyOnWriteArrayList(读)、ConcurrentLinkedQueue
• 减少锁粒度,ConcurrentHashMap分离锁
• 减少锁持有时间,最小化同步代码块
• 乐观CAS锁,AtomicInteger、AtomicLong、AtomicReference
• 自旋锁,JVM层面锁优化,-XX:+UseSpinning开启自旋锁
自旋锁使得在线程没有获得锁情况下不被挂起而是转去执行空循环
线程挂起、恢复开销很大
适用于访问共享资源时间较短情况
• 锁消除,JVM层面锁优化,-server –XX:+DoEscapeAnalysis –XX:+EliminateLocks
开启
• 偏向锁,JVM层面锁优化,-XX:+UseBiasedLocking
35
- 36. 代码调优 – 其他小Tip
• 尽量使用JDK中提供的API,比如Arrays.sort、
System.arrayCopy(native方法)
• 对于重量级对象创建可以用clone()代替new
• 静态方法代替实例方法
• 嵌套循环中将忙循环放在内层
• …
36
- 37. JVM调优 – JMM
• 程序计数器
• Java虚拟机栈
• 本地方法栈(对应native方法)
• 堆
分代:新生代(Eden、s0、s1)、老年代
• 方法区
又称永久区或持久代,主要存放常量信息和类信息
Hot Spot虚拟机中方法区也会进行GC
37
- 38. JVM调优 – JVM内存分配参数
• 堆内存:-Xmx、-Xms
• 新生代:-Xmn,一般设置为整个堆空间的1/4到1/3
Hot Spot虚拟机中-XX:NewSize、-XX:MaxNewSize
• 持久代:-XX:PermSize、-XX:MaxPermSize
• 线程栈:-Xss
• 堆比例参数
-XX:SurvivorRatio = eden / s0 = eden / s1
-XX:NewRatio = 老年代 / 新生代
38
- 39. JVM调优 – GC算法
• Mark-Sweep算法:会导致不连续内存空间
• Copy算法:比Mark-Sweep高效,新生代GC算法
• Mark-Compact算法:基于Mark-Sweep改进的算法,第一阶
段Mark与Mark-Sweep基本相同,但第二阶段压缩存活对象
到内存一端,之后清理边界外的所有其他空间。老年代GC
算法
39
- 40. JVM调优 – GC相关参数
• 堆Dump:-XX:-HeapDumpOnOutOfMemoryError -
XX:HeapDumpPath=/var/heapdump
• 获取GC信息:-verbose:gc或-XX:+PrintGC或-
XX:+PrintGCDetails,-XX:PrintHeapAtGC、-
XX:PrintGCApplicationStoppedTime
• 类和对象跟踪:-XX:+TraceClassLoading、-
XX:+TraceClassUnloading
• …
40
- 41. JVM调优 – GC友好Tip
• GC分代假设:绝大部分对象的生命周期都很短
• 分配小对象开销很小,不要吝啬去创建小对象
• Young GC速度远高于Full GC,尽量避免对象提升到老年代,
Young GC应该占绝大部分,Full GC应该很少
让对象生命周期尽可能短,避免生命周期过长被提升到老年代
为新生代预留足够空间,使大部分对象停留在新生代
尽量避免短命大对象,使用-XX:PretenureSizeThreshold设置大对象
直接进入老年代的阈值
• 避免堆震荡,设置相同的-Xmx和-Xms,可以减少GC次数
• 不可变对象对GC友好:减少卡表扫描
• …
41
- 43. 参考资料
• 《Java程序性能优化》 葛一鸣
• http://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-
ladis2009.pdf
• http://blog.hesey.net/2014/05/gc-oriented-java-programming.html
• http://www.javaworld.com/article/2078623/core-java/jvm-
performance-optimization--part-1--a-jvm-technology-primer.html
• http://www.javaworld.com/article/2076539/build-ci-sdlc/java-
performance-programming--part-1--smart-object-management-
saves-the-day.html
• http://www.javaworld.com/article/2074893/build-ci-sdlc/design-
for-performance--part-1--interfaces-matter.html
• http://www.ibm.com/developerworks/cn/java/l-javaio/
• …
43
Editor's Notes
- 吞吐量:相当一段时间内测量出来的系统单位时间处理的任务数或事务数(TPS)
延迟: 一般包括单向延迟(One-way Latency)和往返延迟(Round Trip Latency),实际测量时一般取往返延迟。
它的单位一般是ms、s、min等。