JVM调优方法
预防
-
规范化的代码,性能更高的代码. 从点滴做起
- 变量作用域
for (int i = 0; i <10000000 ; i++) { int mid = 10000; }
- 字符串的操作建议使用Stringbuffer 或者 StringBuilder
front =front + “hello” + “wolrd” + “a” + “b” + “c” + “d”;
会在常量池中建立 hello wolrd a b c d字符串常量。这意味着,用String拼接字符串可能会导致常量池中产生大量的无用的常量,消耗内存空间,虽然一两个String不算什么,但是积少成多,代码中各个地方都使用String拼接字符串,那就是极大的消耗。- 迭代器的迭代的优雅写法
- ArrayList的实现 以及Spring IOC容器中Bean的lazy策略
通过代码ArrayList list = new ArrayList();并不会完成底层的数据对象的构建而是在第一个使用Add方法的过程中才会完成数据对象的构建.
- 单例模式双重检查(double-checked locking)
- 高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能
锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁同步的集合工具类的和concurrentHashMap
- 创建线程或线程池时请指定有意义的线程名称,方便出错时追溯原因
class MyThead extends Thread { public MyThead() { super.setName("MyThread"); } }
规范化的代码如何培养,可以多去看看《EFFECTIVE JAVA》。多去看看JDK设计者的源码实现.
多去泡泡那些开源框架的源码(看源码晕,也要坚持.) 养成好的编程习惯和编程规范.勤写注释. -
选择适合的硬件设施和软件版本
针对自己的业务做好硬件规划.如我们支撑1000W的TPS给我一 台很鸡肋的服务器那肯定也是不可能的.提前基于业务做好硬件的规划.如 CPU密集型的业务选择好的CPU.IO密集型的业务选择好的IO磁盘等.
在JVM中尽可能的增加堆的空间大小.
在软件版本和选择上也是要稳中求稳.不追新,不过气.
如Tomcat虽然都可能搭建服务器. 8.0之后版本采用的是默认NIO的IO方式.处理性能大大提高.
如Netty 最高的版本是5.X.但是在官方5.0发布之后的一段时间, 就宣布停止了对Netty5.X版本的维护.
如JDK版本的选择,尽量大家选择口碑,运用多的版本.
- 做好高并发高可用的架构和压力测试
作为一个架构设计者功能性需求的不偏不倚是你的本份,非功能 性需求的设计和保证是你的价值体现.
系统的高可用,高并发,高性能 这三高指标做为设计者一定要站在一个全局的高度做全局的把控.让一切风险规避在襁褓中.
- 架设监控系统,进行预警和问题及时排查
鹰眼,普罗米修斯,Zabbix
问题排查与分析
- CPU飙高
基于top 命令排查进程,
基于top -Hp 找到CPU占用过多的线程结合工具进行代码的排查.
- 线程阻塞/线程死锁
使用jstack或者常见的工具(Jvisualvm 或者 arthas)进行线程状态的排查.锁的情况
- JVM 出现OOM
java.lang.OutOfMemoryError: java heap space
heap溢出.检查是否存在内存泄漏问题. 分析堆栈对象情况
java.lang.OutOfMemoryError: Meta space
可通过JVM类加载的情况进行排查.jstat或者实时监测工具排查…
代码层面检查是否通过字节码生成工具生成了大量的类文件、是否通过反射的类和调用的方法较多,大量动态代理的proxy类字 节码生成.
java.lang.OutOfMemoryError:GC over head limit exceeded
系统处于高频的GC状态,而且回收的效果依然不佳的情况 分析GC日志曲线集合堆栈分析排查.
- 业务响应缓慢
1,本身机器性能不够.应对不了高并发压力.小马拉大车肯定拉不 动 2,调用链条出现短板效应.
可通过分析线程状态排查. IO过多 , IO能力不够3.GC收集过程.暂停时间过长.
首先还是需要分析GC日志.基于分析结果可以找到大致原因.如内 存分配不够(JVM一启动占用了20M.你一共分了25M.),或者是垃圾收集器选择不合理.
选择暂停时间优先垃圾回收器,或者暂停时间可控的垃圾回收器
(CMS 或者 G1)
CMS也需要当心 GC并发模式失败(碎片化严重) concurrent mode failure 退化成Serial垃圾收集器
解决方案
- 基于业务特点和监控数据 调整合适的JVM参数
- 开启栈上分配 默认开启
- 开启TLAB 默认开启
- 允许自适应调整各空间比例 默认开启
- 设置最小堆,最大堆一致.防止堆伸缩带来的性能消耗.
- 设置合适的栈的大小.保证在3000-5000的栈的深度
- 针对垃圾回收器,和CPU硬件的情况设置合适的垃圾线程数量.
- 针对业务的特点和程序中对象的特点设置合理的老年代对象临界年龄
- 基于业务和内存的大小选择正确的垃圾回收器
小内存(500M以内),客户机运行我们的win程序 我们可以选择
Serial 系列.
中型内存(500M-4G),服务端,我们可以选择PS PO这样的并行的追求吞吐量的垃圾收集器
大型内存(4G以上),服务端,JDK1.8以上,我们一定要选择G1.
- 从软件设计层面的规避问题和优化
- 针对大并发系统:设计浏览器缓存,本地缓存,验证码,限流组件 引入等等
- 热点服务接口要做到支持防刷,幂等等相关策略.
- 合理的业务垂直划分,减少单个JVM服务逻辑的臃肿和集中.
- 服务端程序设置合理的服务线程数量及线程复用机制,如tomcat调优.
- 使用集群+负载均衡的服务部署方案,减少单点性能瓶颈和并发压力
- 调用链条过长的业务,可以使用消息队列实现异步消息.可减少栈调用的深度
最后终极方案(千万别学)
面向百度编程…工作生产环境多重启.