一、什么是JVM调优?
根据需求进行预规划
运行中的JVM环境优化
解决JVM运行过程中出现的各种问题
二、垃圾回收器组合参数设定:
1、常见垃圾回收器组合及其参数怎么设置:
-XX:+UseSerialGC: 相当于SerialNew + SerialOld的组合,单线程,适用于客户端程序
-XX:+UseParNewGC: 相当于ParNew + SerialOld组合(老年代默认是SerialOld),该组合已经很少用,部分版本已经废弃;
-XX:+UseConc(urrent)MarkSweepGC: (有些版本可能写法不一样,需要加urrent)相当于ParNew + CMS + SerialOld(当CMS并发失败的时候会启用SerialOld);
-XX:+UseParallelGC: 相当于Parallel Scavenge + Parallel Old SerialOld (1.7以前的版本)
-XX:+UseParallelOldGC: 相当于Parallel Scavenge +Parallel Old(1.8以上的版本,相当于PS + PO组合);
-XX:+UseG1GC: 就是Garbage First;
2、在Linux中怎么查看GC的设置:
java +XX:+PrintCommandLineFlags -version
3、HotSpot参数分类:
标准参数:以-开头,所有的HotSpot都支持
非标准参数:-X开头,特定版本执行
不稳定参数:以-XX开头
设置某个参数打开用+,关闭某个参数值用-,如:+UseG1GC和-UseG1GC
4、内存溢出和内存泄漏
内存溢出:Out Of Memary,是指内存空间已经被占用满了无法再分配新的对象;
内存泄漏:Memary Leak,是指有部分对象占用某块内存,不释放也不回收,一直在那占着,少量的内存泄漏并不会导致内存溢出,大量的内存泄漏可能会导致空间被占满,内存溢出;
5、常见参数设定:
一些常见的JVM参数设置:
java -XX: +PrintCommandLineFlags Test 运行Test这个类的main方法时,打印相关参数;
-Xmn10 新生代内存,n代表new
-Xms40 初始堆内存
-Xmx40 最大堆内存
-XX: +PrintCommandLineFlags 开启打印参数
-XX:+PrintGC Test 开启打印GC的信息
-XX:+PrintGCDetails 打印GC的详细信息
-XX:+PrintGCTimestamps 打印GC的时间戳
-XX:+PrintGCCauses 打印GC的原因;
-XX:PrintFlagsInitial
-XX:PrintFlagsFinal 查询全部的、最终的JVM参数,在linux上可以使用grep进行过滤,有七八百个参数:
注意:初始堆内存和最大堆内存一般情况下设置一样大小,如果不设置一样,在内存分配的时候会进行弹性的伸缩,占用计算资源进行内存的扩展和收缩;
6、实战(GC设置及GC信息打印解读):
1、代码:
public static void main(String[] args) {
System.out.println("===执行循环===");
//声明链表
List list = new LinkedList();
for (; ; ) {
//不停地往里面添加数据,每次1M,直到内存溢出
byte[] bytes = new byte[1024 * 1024];
list.add(bytes);
}
}
2、Idea设置VM参数:
3、运行及运行结果:
4、上述结果打印的是简版的GC回收信息,-XX:+PrintGCDetails (Idea有提示)打印的是详细的GC回收信息:
5、将垃圾回收器设置为CMS:
同样的代码,CMS的回收次数更多:
三、调优之前的准备:
1、吞吐量:
用户代码执行时间 / (用户代码执行时间 + 垃圾回收时间)
也就是执行用户代码的时间占总时间的比值,执行用户代码所用的时间越多,说明执行有效业务的时间越多;可以理解为用户代码执行时间的占比,就是到底是用户代码执行时间多还是垃圾回收时间多,(频繁GC的情况下,用户代码执行的时间占比就会减少)
2、响应时间:STW越短,停顿时间越短,响应越快
- 所谓调优,是指在二者之间,根据业务进行一个平衡和调整。
- 首先得根据业务确定程序追求的是吞吐量还是响应时间
- 数据分析,计算,数据挖掘一般都追求吞吐量,在垃圾回收器上首选PC +PO;
- 用户网站,API接口服务,一般追求响应时间,1.8以上可以选ParNew + CMS,或者直接选G1
四、调优:
1、根据需求进行预规划
2、运行中的JVM环境优化
3、解决JVM运行过程中出现的各种问题(比如OOM)
4、对高并发的理解:百万并发是指在1秒钟内,100万个订单同时下下来,也就是TPS,订单提交到持久化过程结束,淘宝最高峰的并发才五六十万,百万并发也是扯淡,12306才能达到;1000的并发已经很不错了
5、调优前必须进行压力测试
6、调优前进行业务规划优化
7、调优步骤:
- 熟悉业务场景
- 选定垃圾回收器(响应时间 or 吞吐量优先)
- 计算内存需求(只能大概估算,比如一个请求需要占用多少内存(分配多少个对象))
- 选定cpu,越高越好,cpu核数高,回收越快
- 设置GC日志参数:(多个日志文件,并循环使用)
五、预调优
调优案例分析:
1、案例1:
百万订单的垂直电商(只卖某类商品),订单系统需要什么样的服务器配置?
分析:
- 每天百万订单,平均到各个时间点,找到一个小时内最高峰的那个点的TPS
- 计算一个订单需要多少内存?512K?不好预估
- 可以要求响应时间在多少毫秒内?进行压力测试
2、案例2:12306春节大规模抢票(更偏向于架构的设计)
- 秒杀系统:CND(各地CND进行缓存) --> LVS--->nginx--->业务系统
- 用Redis抗高并发,单机Redis能支撑1W并发,下订单-减库存-等待用户付款使用异步进行
- 大流量的处理办法:进行分流
- 当出现数据倾斜的时候,有一台专门的服务器进行数据的平衡调度;
3、优化JVM运行环境(慢和卡顿)
面试问题一:
一个在线文档浏览网站,单节点,升级了系统资源(内存)反而卡顿频繁?
因为在线文档需要把文档Loading到内存,对象比较大,一般直接分配到老年代,回收次数频繁,但因为内存小,所以回收时间短,卡顿不明显,反而内存大的时候,回收一次的耗时要比回收多次的STW长,造成资源升级了反而卡顿;
解决:
- 在部署方式上,采用多个JVM组成逻辑集群+反向代理的方式,每个JVM仍然是升级前的内存,进行集群部署;
- 每个JVM在选择垃圾回收器时,选择响应优先的垃圾回收器,比如ParNew+CMS,和G1;
面试问题二:
- 系统CPU经常100%,如何查找,如何调优?(相对内存占满简单)
一定是由线程一直占用系统资源
1、找出占用cpu的进程,使用top命令找出cup占用最多的进程
2、找出进程中哪个线程占用高?top -Hp PID查看PID进程中的所有线程,看进程中哪个线程占比高,记录线程号(也是PID)
3、如果是java程序,导出该线程的堆栈信息,jstack -l pid导出线程堆栈信息
4、jstack会输出线程状态,如果一直waiting或者blocked,说明那个线程可能有问题(堆栈信息中的线程号是16进制,需要转成10进制才能与top -Hp中的线程号对应起来)
jstack -dump
堆栈信息中,重点看waiting on<0x00000123>说明这个线程在等待某个锁,找到哪个线程持有这个锁没释放,<0x00000123>就是那个锁对象
5、可能是工作线程占比高,也可能是垃圾回收线程占比高
为什么阿里的规范里面,要求写线程或线程池的时候,要求自定义线程的名称?
就是为了方便定位OOM问题
- 系统内存飙高,如何查找,如何解决?
1、同样找出进程和线程
2、导出堆内存,一定是堆内存占用较多
3、分析
面试:有个G1,为什么还要CMS?因为响应时间,G1快一些,但吞吐量G1比CMS少15%