JVM调优的本质:
并不是显著的提高系统性能,不是说你调了,性能就能提升几倍或者上十倍,JVM调优,主要调的是稳定。如果你的系统出现了频繁的垃圾回收,这个时候系统是不稳定的,所以需要我们来进行JVM调优,调整垃圾回收的频次。
GC调优原则
调优的原则
- 大多数的java应用不需要GC调优
- 大部分需要GC调优的,不是参数问题,是代码问题
- 在实际使用中,分析GC情况优化代码比优化GC参数要多得多
- GC调优是最后的手段
目的
GC的时间够小
GC的次数够少
发生Full GC的周期足够的长,时间合理,最好是不发生。
注:如果满足下面的指标,则一般不需要进行GC:
- Minor GC执行时间不到50ms
- Minor GC执行不频繁,约10秒一次
- Full GC执行时间不到1s
- Full GC执行频率不算频繁,不低于10分钟1次
GC调优
调优步骤
日志分析
- 监控GC的状态
使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,判断是否需要进行优化 - 分析结果,判断是否需要优化
如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化;如果GC时间超过1-3秒,或者频繁GC,则必须优化 - 调整GC类型和内存分配
如果内存分配过大或过小,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找1台或几台机器进行beta,然后比较优化过的机器和没有优化的机器的性能对比,并有针对性的做出最后选择; - 不断地分析和调整
通过不断的试验和试错,分析并找到最合适的参数 - 全面应用参数
如果找到了最合适的参数,则将这些参数应用到所有服务器,并进行后续跟踪。
阅读GC日志
主要关注MinorGC和FullGC的回收效率(回收前和回收后的空间大小比较)、回收的时间。
推荐策略
-
新生代大小选择
• 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择).在此种情况下,新生代收集发生的频率也是最小的.同时,减少到达老年代的对象.
• 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度.因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用.
• 避免设置过小.当新生代设置过小时会导致:1.MinorGC次数更加频繁 2.可能导致MinorGC对象直接进入老年代,如果此时老年代满了,会触发FullGC. -
老年代大小选择
响应时间优先的应用:老年代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数.如果堆设置小了,可以会造成内存碎 片,高回收频率以及应用暂停而使用传统的标记清除方式;
如果堆大了,则需要较长的收集时间.最优化的方案,一般需要参考以下数据获得:
并发垃圾收集信息、持久代并发收集次数、传统GC信息、花在新生代和老年代回收上的时间比例。
吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的新生代和一个较小的老年代.原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而老年代尽存放长期存活对象
GC调优是个很复杂、很细致的过程,要根据实际情况调整,不同的机器、不同的应用、不同的性能要求调优的手段都是不同的,king老师也无法告诉大家全部,即使是jvm参数也是如此,比如说性能有关的操作系统工具,和操作系统本身相关的所谓大页机制,都需要大家平时去积累,去观察,去实践,king老师在这个专题上告诉大家的除了各种java虚拟机基础知识和内部原理,也告诉大家一个性能优化的一个基本思路和着手的方向。
常用的性能评价/测试指标
一个web应用不是一个孤立的个体,它是一个系统的部分,系统中的每一部分都会影响整个系统的性能
响应时间
提交请求和返回该请求的响应之间使用的时间,一般比较关注平均响应时间。
常用操作的响应时间列表:
并发数
同一时刻,对服务器有实际交互的请求数。
和网站在线用户数的关联:1000个同时在线用户数,可以估计并发数在5%到15%之间,也就是同时并发数在50~150之间。
吞吐量
对单位时间内完成的工作量(请求)的量度
关系
系统吞吐量和系统并发数以及响应时间的关系:
理解为高速公路的通行状况:
吞吐量是每天通过收费站的车辆数目(可以换算成收费站收取的高速费),
并发数是高速公路上的正在行驶的车辆数目,
响应时间是车速。
车辆很少时,车速很快。但是收到的高速费也相应较少;随着高速公路上车辆数目的增多,车速略受影响,但是收到的高速费增加很快;随着车辆的继续增加,车速变得越来越慢,高速公路越来越堵,收费不增反降;如果车流量继续增加,超过某个极限后,任务偶然因素都会导致高速全部瘫痪,车走不动,当然后也收不着,而高速公路成了停车场(资源耗尽)。
常用的性能优化手段
避免过早优化
不应该把大量的时间耗费在小的性能改进上,过早考虑优化是所有噩梦的根源。
所以,我们应该编写清晰,直接,易读和易理解的代码,真正的优化应该留到以后,等到性能分析表明优化措施有巨大的收益时再进行。
但是过早优化,不表示我们就可以随便写代码,还是需要注重编写高效优雅的代码。
进行系统性能测试
所有的性能调优,都有应该建立在性能测试的基础上,直觉很重要,但是要用数据说话,可以推测,但是要通过测试求证。
寻找系统瓶颈,分而治之,逐步优化
性能测试后,对整个请求经历的各个环节进行分析,排查出现性能瓶颈的地方,定位问题,分析影响性能的的主要因素是什么?内存、磁盘IO、网络、CPU,还是代码问题?架构设计不足?或者确实是系统资源不足?
前端优化常用手段
浏览器/App
减少请求数
合并CSS,Js,图片,
生产服务器提供的all的js文件
http中的keep-alive(http1.1中默认开启)包括nginx
使用客户端缓冲
静态资源文件(css、图标等)缓存在浏览器中,有关的属性Cache-Control(相对时间)和Expires
如果文件发生了变化,需要更新,则通过改变文件名来解决。
启用压缩
浏览器(zip),压缩率80%以上。
减少网络传输量,但会给浏览器和服务器带来性能的压力,需要权衡使用。
资源文件加载顺序
css放在页面最上面,js放在最下面。这样页面的体验才会比较好。
浏览器会加载完CSS才会对页面进行渲染
JS只要加载后就会立刻执行。(有些JS可能执行时间比较长)
减少Cookie传输
cookie包含在每次的请求和响应中,因此哪些数据写入cookie需要慎重考虑(静态资源不需要放入cookie)
友好的提示(非技术手段)
有时候在前端给用户一个提示,就能收到良好的效果。毕竟用户需要的是不要不理他。
CDN加速
CDN,又称内容分发网络,本质是一个缓存,而且是将数据缓存在用户最近的地方。无法自行实现CDN的时候,可以根据经济实力考虑商用CDN服务。
反向代理缓存
将静态资源文件缓存在反向代理服务器上,一般是Nginx。
WEB组件分离
将js,css和图片文件放在不同的域名下。可以提高浏览器在下载web组件的并发数。因为浏览器在下载同一个域名的的数据存在并发数限制。
应用服务性能优化
缓存
网站性能优化第一定律:优先考虑使用缓存优化性能
优先原则:缓存离用户越近越好
缓存的基本原理和本质
缓存是将数据存在访问速度较高的介质中。可以减少数据访问的时间,同时避免重复计算。
合理使用缓存的准则
频繁修改的数据,尽量不要缓存,读写比2:1以上才有缓存的价值。
缓存一定是热点数据。
应用需要容忍一定时间的数据不一致。
缓存可用性问题,一般通过热备或者集群来解决。
分布式缓存与一致性哈希
以集群的方式提供缓存服务,有两种实现;
- 需要更新同步的分布式缓存,所有的服务器保存相同的缓存数据,带来的问题就是,缓存的数据量受限制,其次,数据要在所有的机器上同步,代价很大。
- 每台机器只缓存一部分数据,然后通过一定的算法选择缓存服务器。常见的余数hash算法存在当有服务器上下线的时候,大量缓存数据重建的问题。所以提出了一致性哈希算法。
一致性哈希:
- 首先求出服务器(节点)的哈希值,并将其配置到0~232次方的圆(continuum)上。
- 然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。
- 然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器,就会保存到第一台服务器上。
一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。
数据倾斜:
一致性哈希算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜问题,此时必然造成大量数据集中到Node A上,而只有极少量会定位到Node B上。为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器ip或主机名的后面增加编号来实现。例如,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,例如定位到“Node A#1”、“Node A#2”、“Node A#3”三个虚拟节点的数据均定位到Node A上。这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。
集群
可以很好的将用户的请求分配到多个机器处理,对总体性能有很大的提升
异步
常见异步的手段
Servlet异步
servlet3中才有,支持的web容器在tomcat7和jetty8以后。
多线程
消息队列
程序
代码级别
一个应用的性能归根结底取决于代码是如何编写的。
选择合适的数据结构
选择ArrayList和LinkedList对我们的程序性能影响很大,为什么?因为ArrayList内部是数组实现,存在着不停的扩容和数据复制。
选择更优的算法
编写更少的代码
同样正确的程序,小程序比大程序要快,这点无关乎编程语言。
并发编程
资源的复用
目的是减少开销很大的系统资源的创建和销毁,比如数据库连接,网络通信连接,线程资源等等。
单例模式
Spring中的bean
池化技术
存储性能优化
尽量使用SSD
定时清理数据或者按数据的性质分开存放
结果集处理
用setFetchSize控制jdbc每次从数据库中返回多少数据。