Java性能调优笔记.docx
- 文档编号:12705914
- 上传时间:2023-04-21
- 格式:DOCX
- 页数:53
- 大小:338.19KB
Java性能调优笔记.docx
《Java性能调优笔记.docx》由会员分享,可在线阅读,更多相关《Java性能调优笔记.docx(53页珍藏版)》请在冰豆网上搜索。
Java性能调优笔记
Java性能调优笔记
分类:
java2010-08-2223:
472729人阅读评论
(2)收藏举报
javaexceptionthreadrandomclassjvm
Java性能调优笔记
调优步骤:
衡量系统现状、设定调优目标、寻找性能瓶颈、性能调优、衡量是否到达目标(如果未到达目标,需重新寻找性能瓶颈)、性能调优结束。
寻找性能瓶颈
性能瓶颈的表象:
资源消耗过多、外部处理系统的性能不足、资源消耗不多但程序的响应速度却仍达不到要求。
资源消耗:
CPU、文件IO、网络IO、内存。
外部处理系统的性能不足:
所调用的其他系统提供的功能或数据库操作的响应速度不够。
资源消耗不多但程序的响应速度却仍达不到要求:
程序代码运行效率不够高、未充分使用资源、程序结构不合理。
CPU消耗分析
CPU主要用于中断、内核、用户进程的任务处理,优先级为中断>内核>用户进程。
上下文切换:
每个线程分配一定的执行时间,当到达执行时间、线程中有IO阻塞或高优先级线程要执行时,将切换执行的线程。
在切换时要存储目前线程的执行状态,并恢复要执行的线程的状态。
对于Java应用,典型的是在进行文件IO操作、网络IO操作、锁等待、线程Sleep时,当前线程会进入阻塞或休眠状态,从而触发上下文切换,上下文切换过多会造成内核占据较多的CPU的使用。
运行队列:
每个CPU核都维护一个可运行的线程队列。
系统的load主要由CPU的运行队列来决定。
运行队列值越大,就意味着线程会要消耗越长的时间才能执行完成。
利用率:
CPU在用户进程、内核、中断处理、IO等待、空闲,这五个部分使用百分比。
文件IO消耗分析
Linux在操作文件时,将数据放入文件缓存区,直到内存不够或系统要释放内存给用户进程使用。
所以通常情况下只有写文件和第一次读取文件时会产生真正的文件IO。
对于Java应用,造成文件IO消耗高主要是多个线程需要进行大量内容写入(例如频繁的日志写入)的动作、磁盘设备本身的处理速度慢、文件系统慢、操作的文件本身已经很大。
网络IO消耗分析
对于分布式Java应用,网卡中断是不是均衡分配到各CPU(cat/proc/interrupts查看)。
内存消耗分析(-Xms和-Xmx设为相同的值,避免运行期JVM堆内存要不断申请内存)
对于Java应用,内存的消耗主要在Java堆内存上,只有创建线程和使用DirectByteBuffer才会操作JVM堆外的内存。
JVM内存消耗过多会导致GC执行频繁,CPU消耗增加,应用线程的执行速度严重下降,甚至造成OutOfMemoryError,最终导致Java进程退出。
JVM堆外的内存
swap的消耗、物理内存的消耗、JVM内存的消耗。
程序执行慢原因分析
锁竞争激烈:
很多线程竞争互斥资源,但资源有限,造成其他线程都处于等待状态。
未充分使用硬件资源:
线程操作被串行化。
数据量增长:
单表数据量太大(如1个亿)造成数据库读写速度大幅下降(操作此表)。
调优
JVM调优(最关键参数为:
-Xms-Xmx-Xmn-XX:
SurvivorRatio-XX:
MaxTenuringThreshold)
代大小调优:
避免新生代大小设置过小、避免新生代大小设置过大、避免Survivor设置过小或过大、合理设置新生代存活周期。
-Xmn调整新生代大小,新生代越大通常也意味着更多对象会在minorGC阶段被回收,但可能有可能造成旧生代大小,造成频繁触发FullGC,甚至是OutOfMemoryError。
-XX:
SurvivorRatio调整Eden区与Survivor区的大小,Eden区越大通常也意味着minorGC发生频率越低,但可能有可能造成Survivor区太小,导致对象minorGC后就直接进入旧生代,从而更频繁触发FullGC。
GC策略的调优:
CMSGC多数动作是和应用并发进行的,确实可以减小GC动作给应用造成的暂停时间。
对于Web应用非常需要一个对应用造成暂停时间短的GC,再加上Web应用的瓶颈都不在CPU上,在G1还不够成熟的情况下,CMSGC是不错的选择。
(如果系统不是CPU密集型,且从新生代进入旧生代的大部分对象是可以回收的,那么采用CMSGC可以更好地在旧生代满之前完成对象的回收,更大程度降低FullGC发生的可能)
在调整了内存管理方面的参数后应通过-XX:
PrintGCDetails、-XX:
+PrintGCTimeStamps、-XX:
+PrintGCApplicationStoppedTime以及jstat或visualvm等方式观察调整后的GC状况。
出内存管理以外的其他方面的调优参数:
-XX:
CompileThreshold、-XX:
+UseFastAccessorMethods、-XX:
+UseBaiasedLocking。
程序调优
CPU消耗严重的解决方法
CPUus高的解决方法:
CPUus高的原因主要是执行线程不需要任何挂起动作,且一直执行,导致CPU没有机会去调度执行其他的线程。
调优方案:
增加Thread.sleep,以释放CPU的执行权,降低CPU的消耗。
以损失单次执行性能为代价的,但由于其降低了CPU的消耗,对于多线程的应用而言,反而提高了总体的平均性能。
(在实际的Java应用中类似场景,对于这种场景最佳方式是改为采用wait/notify机制)
对于其他类似循环次数过多、正则、计算等造成CPUus过高的状况,则需要结合业务调优。
对于GC频繁,则需要通过JVM调优或程序调优,降低GC的执行次数。
CPUsy高的解决方法:
CPUsy高的原因主要是线程的运行状态要经常切换,对于这种情况,常见的一种优化方法是减少线程数。
调优方案:
将线程数降低
这种调优过后有可能会造成CPUus过高,所以合理设置线程数非常关键。
对于Java分布式应用,还有一种典型现象是应用中有较多的网络IO操作和确实需要一些锁竞争机制(如数据库连接池),但为了能够支撑搞得并发量,可采用协程(Coroutine)来支撑更高的并发量,避免并发量上涨后造成CPUsy消耗严重、系统load迅速上涨和系统性能下降。
在Java中实现协程的框架有Kilim,Kilim执行一项任务创建Task,使用Task的暂停机制,而不是Thread,Kilim承担了线程调度以及上下切换动作,Task相对于原生Thread而言就轻量级多了,且能更好利用CPU。
Kilim带来的是线程使用率的提升,但同时由于要在JVM堆中保存Task上下文信息,因此在采用Kilim的情况下要消耗更多的内存。
(目前JDK7中也有一个支持协程方式的实现,另外基于JVM的Scala的Actor也可用于在Java使用协程)
文件IO消耗严重的解决方法
从程序的角度而言,造成文件IO消耗严重的原因主要是多个线程在写进行大量的数据到同一文件,导致文件很快变得很大,从而写入速度越来越慢,并造成各线程激烈争抢文件锁。
常用调优方法:
异步写文件
批量读写
限流
限制文件大小
网络IO消耗严重的解决方法
从程序的角度而言,造成网络IO消耗严重的原因主要是同时需要发送或接收的包太多。
常用调优方法:
限流,限流通常是限制发送packet的频率,从而在网络IO消耗可接受的情况下来发送packget。
内存消耗严重的解决方法
释放不必要的引用:
代码持有了不需要的对象引用,造成这些对象无法被GC,从而占据了JVM堆内存。
(使用ThreadLocal:
注意在线程内动作执行完毕时,需执行ThreadLocal.set把对象清除,避免持有不必要的对象引用)
使用对象缓存池:
创建对象要消耗一定的CPU以及内存,使用对象缓存池一定程度上可降低JVM堆内存的使用。
采用合理的缓存失效算法:
如果放入太多对象在缓存池中,反而会造成内存的严重消耗,同时由于缓存池一直对这些对象持有引用,从而造成FullGC增多,对于这种状况要合理控制缓存池的大小,避免缓存池的对象数量无限上涨。
(经典的缓存失效算法来清除缓存池中的对象:
FIFO、LRU、LFU等)
合理使用SoftReference和WeekReference:
SoftReference的对象会在内存不够用的时候回收,WeekReference的对象会在FullGC的时候回收。
资源消耗不多但程序执行慢的情况的解决方法
降低锁竞争:
多线多了,锁竞争的状况会比较明显,这时候线程很容易处于等待锁的状况,从而导致性能下降以及CPUsy上升。
使用并发包中的类:
大多数采用了lock-free、nonblocking算法。
使用Treiber算法:
基于CAS以及AtomicReference。
使用Michael-Scott非阻塞队列算法:
基于CAS以及AtomicReference,典型ConcurrentLindkedQueue。
(基于CAS和AtomicReference来实现无阻塞是不错的选择,但值得注意的是,lock-free算法需不断的循环比较来保证资源的一致性的,对于冲突较多的应用场景而言,会带来更高的CPU消耗,因此不一定采用CAS实现无阻塞的就一定比采用lock方式的性能好。
还有一些无阻塞算法的改进:
MCAS、WSTM等)
尽可能少用锁:
尽可能只对需要控制的资源做加锁操作(通常没有必要对整个方法加锁,尽可能让锁最小化,只对互斥及原子操作的地方加锁,加锁时尽可能以保护资源的最小化粒度为单位--如只对需要保护的资源加锁而不是this)。
拆分锁:
独占锁拆分为多把锁(读写锁拆分、类似ConcurrentHashMap中默认拆分为16把锁),很多程度上能提高读写的性能,但需要注意在采用拆分锁后,全局性质的操作会变得比较复杂(如ConcurrentHashMap中size操作)。
(拆分锁太多也会造成副作用,如CPU消耗明显增加)
去除读写操作的互斥:
在修改时加锁,并复制对象进行修改,修改完毕后切换对象的引用,从而读取时则不加锁。
这种称为CopyOnWrite,CopyOnWriteArrayList是典型实现,好处是可以明显提升读的性能,适合读多写少的场景,但由于写操作每次都要复制一份对象,会消耗更多的内存。
充分利用硬件资源(CPU和内存):
充分利用CPU
在能并行处理的场景中未使用足够的线程(线程增加:
CPU资源消耗可接受且不会带来激烈竞争锁的场景下),例如单线程的计算,可以拆分为多个线程分别计算,最后将结果合并,JDK7中的fork-join框架。
Amdahl定律公式:
1/(F+(1-F)/N)。
充分利用内存
数据的缓存、耗时资源的缓存(数据库连接创建、网络连接的创建等)、页面片段的缓存。
毕竟内存的读取肯定远快于硬盘、网络的读取,在内存消耗可接受、GC频率、以及系统结构(例如集群环境可能会带来缓存的同步)可接受情况下,应充分利用内存来缓存数据,提升系统的性能。
总结:
好的调优策略是收益比(调优后提升的效果/调优改动所需付出的代价)最高的,通常来说简单的系统调优比较好做,因此尽量保持单机上应用的纯粹性,这是大型系统的基本架构原则。
调优的三大有效原则:
充分而不过分使用硬件资源、合理调整JVM、合理使用JDK包。
学习参考资料:
《分布式Java应用:
基础与实践》
补充《分布式Java应用:
基础与实践》一些代码样例:
cpu-----------------------------------
CpuNotUseEffectiveDemo
[java]viewplaincopyprint?
1./**
2.*
3.*/
4.packagetune.program.cpu;
5.importjava.util.ArrayList;
6.importjava.util.List;
7.importjava.util.Random;
8./**
9.*未充分利用CPU:
在能并行处理的场景中未使用足够的线程(线程增加:
CPU资源消耗可接受且不会带来激烈竞争锁的场景下)
10.*
11.*@authoryangwmAug25,20109:
54:
50AM
12.*/
13.publicclassCpuNotUseEffectiveDemo{
14.
15.privatestaticintexecuteTimes=10;
16.privatestaticinttaskCount=200;
17.
18.publicstaticvoidmain(String[]args)throwsException{
19.Tasktask=newTask();
20.for(inti=0;i 21.task.addTask(Integer.toString(i)); 22.} 23. 24.longbeginTime=System.currentTimeMillis(); 25.for(inti=0;i 26.System.out.println("Round: "+(i+1)); 27.Threadthread=newThread(task); 28.thread.start(); 29.thread.join(); 30.} 31.longendTime=System.currentTimeMillis(); 32.System.out.println("Executesummary: Round("+executeTimes+")TaskCountPerRound("+taskCount 33.+")ExecuteTime("+(endTime-beginTime)+")ms"); 34.} 35. 36.staticclassTaskimplementsRunnable{ 37.List 38.Randomrandom=newRandom(); 39.booleanexitFlag=false; 40. 41.publicvoidaddTask(Stringtask){ 42.List 43.copyTasks.add(task); 44. 45.tasks=copyTasks; 46.} 47.@Override 48.publicvoidrun(){ 49.List 50.List 51.for(Stringtask: runTasks){ 52.try{ 53.Thread.sleep(random.nextInt(10)); 54.}catch(Exceptione){ 55.e.printStackTrace(); 56.} 57. 58.removeTasks.add(task); 59.} 60. 61.try{ 62.Thread.sleep(10); 63.}catch(Exceptione){ 64.e.printStackTrace(); 65.} 66.} 67. 68.} 69. 70.} 71./* 72.Round: 1 73....... 74.Round: 10 75.Executesummary: Round(10)TaskCountPerRound(200)ExecuteTime(10687)ms 76.*/ CpuUseEffectiveDemo [java]viewplaincopyprint? 1./** 2.* 3.*/ 4.packagetune.program.cpu; 5.importjava.util.ArrayList; 6.importjava.util.List; 7.importjava.util.Random; 8.importjava.util.concurrent.CountDownLatch; 9. 10./** 11.*充分利用CPU: 在能并行处理的场景中使用足够的线程(线程增加: CPU资源消耗可接受且不会带来激烈竞争锁的场景下) 12.* 13.*@authoryangwmAug25,20109: 54: 50AM 14.*/ 15.publicclassCpuUseEffectiveDemo{ 16. 17.privatestaticintexecuteTimes=10; 18.privatestaticinttaskCount=200; 19.privatestaticfinalintTASK_THREADCOUNT=16; 20.privatestaticCountDownLatchlatch; 21. 22.publicstaticvoidmain(String[]args)throwsException{ 23.Task[]tasks=newTask[TASK_THREADCOUNT]; 24.for(inti=0;i 25.tasks[i]=newTask(); 26.} 27.for(inti=0;i 28.intmod=i%TASK_THREADCOUNT; 29.tasks[mod].addTask(Integer.toString(i)); 30.} 31. 32.longbeginTime=System.currentTimeMillis(); 33.for(inti=0;i 34.System.out.println("Round: "+(i+1)); 35.latch=newCountDownLatch(TASK_THREADCOUNT); 36.for(intj=0;j 37.Threadthread=newThread(tasks[j]); 38.thread.start(); 39.} 40.latch.await(); 41.} 42.longendTime=System.currentTimeMillis(); 43.System.out.println("Executesummary: Round("+executeTimes+")TaskCountPerRound("+taskCount 44.+")ExecuteTime("+(endTime-beginTime)+")ms"); 45.} 46. 47.staticclassTaskimplementsRunnable{ 48.List 49.Randomrandom=newRandom(); 50.booleanexitFlag=false; 51. 52.publicvoidaddTask(Stringtask){ 53.List 54.copyTasks.add(task); 55. 56.tasks=copyTasks; 57.} 58.@Override 59.publicvoidrun(){ 60.List 61.List 62.for(Stringtask: runTasks){ 63.try{ 64.Thread.sleep(random.nextInt(10)); 65.}catch(Exceptione){ 66.e.printStackTrace(); 67.} 68. 69.removeTasks.add(task); 70.} 71. 72.try{ 73.Thread.sleep(10); 74.}catch(Exceptione){ 75.e.printStackTrace(); 76.} 77.latch.countDown(); 78.} 79. 80.} 81. 82.} 83./* 84.Round: 1 85....... 86.Round: 10 87.Executesummary: Round(10)TaskCountPerRound(200)ExecuteTime(938)ms 88.*/ fileio------------------------------------------------------------------- IOWaitHighDemo [java]viewplaincopyprint? 1./** 2.* 3.*/ 4.packagetune.program.fileio; 5.importjava.io.BufferedWriter; 6.importjava.io.File; 7.importjava.io.FileWriter; 8.importjava.util.Random; 9./** 10.*文件IO消耗严重的原因主要是多个线程在写进行大量的数据到同一文件, 11.*导致文件很快变得很大,从而写入速度越来越慢,并造成各线程激烈争抢文件锁。 12.* 13.*@authoryangwmAug
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 性能 笔记