生产稳定:JVM-MySQL-Tomcat-服务调用,调优相关

一、JVM

1、开始调优

  1. 根据需求进行JVM规划和预调优,这个其实后置了;
  2. 优化运行JVM运行环境(慢,卡顿),内存不停增长;
  3. 解决JVM运行过程中出现的各种问题(OOM);

2、当前运行环境和配置 以及监控告警

监控告警:目前采用的是单ECS实例部署一个服务的方式,通过对硬件内存的监控来做的告警。
可以直接监控JVM:Grafana + Prometheus 监控JVM

配置参数调整和测试:
增加gc 日志和 内存设置:

-Xloggc:/data/logs/gc/xxx-service-gc-%t.log -Xmn768m -Xms1024m -Xmx1024m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=100m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/xxx-heapdump.hprof -XX:ErrorFile=/var/log/hs_err_pid_%p.log -jar /data/jar/${service}.jar &> /dev/null  &

常见配置汇总

堆设置
-Xms:初始堆大小
-Xmx:最大堆大小
  #通常上面两个参数设置为一样的值,为了避免在生产环境由于heap内存扩大或缩小导致应用停顿,降低延迟,同时避免每次垃圾回收完成后JVM重新分配内存。
  
-Xmn:年轻代大小(这个是我们要关注和处理的,设置的太小,ygc频繁,最终导致对象年龄达到old区阈值就会引发fgc发生;设置太大也会增加ygc的时长,降低效率;我们的目标是减少ygc发生,避免fgc产生;也就是让短生命周期的对象,在ygc阶段就回收掉)

-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:3,表示年轻代与年老代比值为13,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示EdenSurvivor=32,一个Survivor区占整个年轻代的1/5

-XX:MaxPermSize=n:设置持久代大小(jdk1.8之前)
-XX:MetaspaceSize=设置元空间大小(jdk1.8之后)
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器(1.8默认年轻代)
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器

PS+PO(1.8默认): 吞吐量优先
ParNew+CMS: 响应时间优先
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器设置
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

JDK8中,XX:MaxMetaspaceSize确实是没有上限的,最大容量与机器的内存有关;但是XX:MetaspaceSize是有一个默认值的:21M
java -XX:+PrintFlagsInitial    可以查看本机的初始化参数,-XX:Metaspacesize21810376B(大约20.8M)
​
-XX:MaxMetaspaceSize=N
这个参数用于限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存,影响到其他程序。在本机上该参数的默认值为4294967295B(大约4096MB)。
​
-XX:MinMetaspaceFreeRatio=N
当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数,那么虚拟机将增长Metaspace的大小。在本机该参数的默认值为40,也就是40%。设置该参数可以控制Metaspace的增长的速度,太小的值会导致Metaspace增长的缓慢,Metaspace的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace增长的过快,浪费内存。
​
-XX:MaxMetasaceFreeRatio=N
当进行过Metaspace GC之后, 会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放Metaspace的部分空间。在本机该参数的默认值为70,也就是70%。
​
-XX:MaxMetaspaceExpansion=N
Metaspace增长时的最大幅度。在本机上该参数的默认值为5452592B(大约为5MB)。
​
-XX:MinMetaspaceExpansion=N
Metaspace增长时的最小幅度。在本机上该参数的默认值为340784B(大约330KB为)。

3、设置规划和预期目标

● 吞吐量throughput(工作时间不算gc的时间占总的时间比);
● 暂停pause(gc发生时app对外显示的无法响应)。

吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代.原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。

server端的app会有以下规则:
首先决定能分配给vm的最大的heap size,
然后设定最佳的young generation的大小;
如果heap size固定后,增加young generation的大小意味着减小tenured generation大小。
让tenured generation在任何时候够大,能够容纳所有live的data(留10%-20%的空余)。

目标:FGC的频率降为0;

场景:
一个订单(请求–正常的http请求,不包含批处理和异步job) 产生需要多少内存?512K,
1000个/1秒请求大概 500M/1秒内存。

4、gc 频率监控,gc 日志分析,

未调整任何参数的情况:

参数调整之后,FGC降为0;YGC的次数也减少很多。

启动命令增加打印gc日志:

-Xloggc:/Users/logs/xxxx-service-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
2021-09-09T14:51:36.620-0800: 42.474: [Full GC (Metadata GC Threshold) [PSYoungGen: 1730K->0K(885248K)] [ParOldGen: 55839K->40336K(143872K)] 57569K->40336K(1029120K), [Metaspace: 56994K->56994K(1099776K)], 0.1724480 secs] [Times: user=1.16 sys=0.01, real=0.17 secs] 
 2021-09-09T14:51:41.385-0800: 47.238: [GC (Allocation Failure) [PSYoungGen: 864768K->20960K(885760K)] 905104K->70071K(1029632K), 0.0228792 secs] [Times: user=0.09 sys=0.02, real=0.02 secs] 
 2021-09-09T14:51:44.646-0800: 50.499: [GC (Allocation Failure) [PSYoungGen: 885728K->26138K(1234944K)] 934839K->88006K(1378816K), 0.0299223 secs] [Times: user=0.12 sys=0.03, real=0.03 secs] 
 2021-09-09T14:51:48.421-0800: 54.275: [GC (Allocation Failure) [PSYoungGen: 1234458K->20187K(1249280K)] 1296326K->95725K(1393152K), 0.0381818 secs] [Times: user=0.14 sys=0.03, real=0.03 secs] 
 2021-09-09T14:51:49.463-0800: 55.317: [GC (Metadata GC Threshold) [PSYoungGen: 281674K->6968K(1365504K)] 357212K->99231K(1509376K), 0.0194562 secs] [Times: user=0.08 sys=0.02, real=0.02 secs] 

5、jprofile监控 jvm实时状态,堆内存、线程、cpu

目前分析占据内存最多的几块
● 页面小红点对应的接口功能,由前端轮询调用,导致内存增加过多;(这个可以改由websocket提供)
● 业务程序和异步定时job共存,xxl的许多job会开启新线程处理,导致内存增加;(这个可以拆分服务)
● 程序内存采用的注册中心常驻内存,这块正常,不需要处理;(只需要对相关参数微调,比如心跳频率和间隔)

6、排查代码

目前这块需要监测到OOM的发生才方便定位错误代码。
OOM 堆转储:

-XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=/data/logs/heapdump.hprof

设置当首次遭遇内存溢出时导出此时堆中相关信息

-XX:+HeapDumpOnOutOfMemoryError

指定导出堆信息时的路径或文件名,通过MAT分析工具分析OOM时的对象数量,内存占用,线程情况等情况;

-XX:HeapDumpPath=/data/heapdump.hprof

OOM后立即执行备份方案:

指定发生内存溢出后执行的脚本,采用内存溢出后将tomcat shutdown并kill,通过linux crontab 做监控,发现访问异常,执行重启,并且使用了钉钉群消息的方式通知相关人员。

-XX:OnOutOfMemoryError='sh /usr/shellfile/xxxxx.sh

7、 常用命令

jinfo 是 JDK 自带的命令,可以用来查看正在运行的 java 应用程序的扩展参数,包括Java System属性和JVM命令行参数;也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息

使用 jinfo 可以在不重启虚拟机的情况下,可以动态的修改 jvm 的参数。尤其在线上的环境特别有用。

命令:jinfo -flag [+|-]name pid
描述:开启或者关闭对应名称的参数

查看jvm系统详细参数及环境变量

jinfo pid

查看最大堆内存
jinfo -flag MaxHeapSize pid
开启dump文件转储
jinfo -flag +HeapDumpOnOutOfMemoryError pid
设置dump文件位置
jinfo -flag HeapDumpPath=/opt/dump pid
jinfo -flags pid

Attaching to process ID 11968, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.172-b11
Non-default VM flags: -XX:CICompilerCount=2 -XX:+HeapDumpOnOutOfMemoryError -XX:InitialHeapSize=31457280 -XX:MaxHeapSize=482344960 -XX:MaxNewSize=160432128 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=10485760 -XX:OldSize=20971520 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
Command line:  
8、JVM内存模型

JVM内存占物理内存比例 50% - 80%

GC常用参数

-Xmn  -Xms  -Xmx  -Xss
年轻代 最小堆 最大堆 栈空间
-XX:+UseTLAB
使用TLAB,默认打开

-XX:+PrintTLAB
打印TLAB的使用情况

-XX:TLABSize
设置TLAB大小

-XX:+DisableExplictGC
System.gc()不管用 ,FGC

-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintHeapAtGC
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationConcurrentTime ()
打印应用程序时间
-XX:+PrintGCApplicationStoppedTime (低)
打印暂停时长
-XX:+PrintReferenceGC (重要性低)
记录回收了多少种不同引用类型的引用
-verbose:class
类加载详细过程
-XX:+PrintVMOptions
-XX:+PrintFlagsFinal  -XX:+PrintFlagsInitial
必须会用
-Xloggc:opt/log/gc.log
-XX:MaxTenuringThreshold
升代年龄,最大值15
锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 ...
这些不建议设置

Parallel常用参数

-XX:SurvivorRatio
-XX:PreTenureSizeThreshold
大对象到底多大
-XX:MaxTenuringThreshold
-XX:+ParallelGCThreads
并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
-XX:+UseAdaptiveSizePolicy
自动选择各区大小比例

CMS常用参数

-XX:+UseConcMarkSweepGC
-XX:ParallelCMSThreads
CMS线程数量
-XX:CMSInitiatingOccupancyFraction
使用多少比例的老年代后开始CMS收集,默认是68%(近似值),如果频繁发生SerialOld卡顿,应该调小,(频繁CMS回收)
-XX:+UseCMSCompactAtFullCollection
在FGC时进行压缩
-XX:CMSFullGCsBeforeCompaction
多少次FGC之后进行压缩
-XX:+CMSClassUnloadingEnabled
-XX:CMSInitiatingPermOccupancyFraction
达到什么比例时进行Perm回收
GCTimeRatio
设置GC时间占用程序运行时间的百分比
-XX:MaxGCPauseMillis
停顿时间,是一个建议时间,GC会尝试用各种手段达到这个时间,比如减小年轻代

G1常用参数

-XX:+UseG1GC
-XX:MaxGCPauseMillis
建议值,G1会尝试调整Young区的块数来达到这个值
-XX:GCPauseIntervalMillis
?GC的间隔时间
-XX:+G1HeapRegionSize
分区大小,建议逐渐增大该值,1 2 4 8 16 32。
随着size增加,垃圾的存活时间更长,GC间隔更长,但每次GC的时间也会更长
ZGC做了改进(动态区块大小)
G1NewSizePercent
新生代最小比例,默认为5%
G1MaxNewSizePercent
新生代最大比例,默认为60%
GCTimeRatio
GC时间建议比例,G1会根据这个值调整堆空间
ConcGCThreads
线程数量
InitiatingHeapOccupancyPercent
启动G1的堆空间占用比例

————————————————————————————————

二、Tomcat

配置文件设置最多能起的线程数和支持的排队数量,超出后拒绝新的请求。

 server:
   port: 8021
   tomcat:
     threads:
       max: 1000
     accept-count: 500

Tomcat最大可能达到的线程数是maxConnections这个参数和并发数,当并发数超过这个参数则请求会排队,这时响应的快慢就看你的程序性能了。
● maxThreads: Tomcat线程池最多能起的线程数;默认200,1个线程1M;
● maxConnections: Tomcat最多能并发处理的请求(连接); 默认8192;
● acceptCount: Tomcat维护最大的队列数;默认100,队列任务1M;
● minSpareThreads: Tomcat初始化的线程池大小或者说Tomcat线程池最少会有这么多线程。默认10;

比较容易弄混的是maxThreads和maxConnections这两个参数:maxThreads是指Tomcat线程池做多能起的线程数,而maxConnections则是Tomcat一瞬间做多能够处理的并发连接数。
比如maxThreads=1000,maxConnections=800,假设某一瞬间的并发时1000,那么最终Tomcat的线程数将会是800,即同时处理800个请求,剩余200进入队列“排队”,如果acceptCount=100,那么有100个请求会被拒掉。
注意:根据前面所说,只是并发那一瞬间Tomcat会起800个线程处理请求,但是稳定后,某一瞬间可能只有很少的线程处于RUNNABLE状态,大部分线程是TIMED_WAITING,如果你的应用处理时间够快的话。所以真正决定Tomcat最大可能达到的线程数是maxConnections这个参数和并发数,当并发数超过这个参数则请求会排队,这时响应的快慢就看你的程序性能了。

————————————————————————————————
SpringBoot支持的三种容器。
默认配置情况下,undertow的吞吐量领先于jetty和tomcat.,
undertow > jetty > tomcat

————————————————————————————————

三、服务调用

1、提升并发数的4个方法
A、让服务能力对等(serviceUrl,打乱顺序, eureka-server);
B、能用多线程,用多线程;
C、增加连接数,tomcat、mysql、redis;
D、服务无状态,便于横向扩展;
2、减少响应时间的6个方法
A、异步(最终一致性)
B、缓存(减少db读取,减少磁盘io,读多写少)
C、数据库优化
D、多的数据,分批次返回
E、减少调用链
F、长连接,不用轮训

————————————————————————————————

四、MySQL

慢sql:
A、查询结果对应字段,只返回需要的字段,不需要查询全部表字段;
B、索引检查,根据explain 执行计划,确认查询条件是否正常命中索引;
C、数据量限制,分页返回或者限制条数;
D、数据量级到达千万级,需要考虑分库或者分表方案;
D-1、垂直分库 - 通常按照业务维度进行分库
D-2、垂直分表 - 通常是多字段的大表,拆分为多个小表;
水平分表 - range范围、唯一id的hash、按时间(年或者月生成)
分库分表带来的join问题和分布式事务问题需要解决。

show status like '%conn%';

常用变量有:

Aborted_clients 由于客户没有正确关闭连接已经死掉,已经放弃的连接数量。
Aborted_connects 尝试已经失败的MySQL服务器的连接的次数。
Connections 试图连接MySQL服务器的次数。
Created_tmp_tables 当执行语句时,已经被创造了的隐含临时表的数量。
Delayed_insert_threads 正在使用的延迟插入处理器线程的数量。
Delayed_writes 用INSERT DELAYED写入的行数。
Delayed_errors 用INSERT DELAYED写入的发生某些错误(可能重复键值)的行数。
Flush_commands 执行FLUSH命令的次数。
Handler_delete 请求从一张表中删除行的次数。
Handler_read_first 请求读入表中第一行的次数。
Handler_read_key 请求数字基于键读行。
Handler_read_next 请求读入基于一个键的一行的次数。
Handler_read_rnd 请求读入基于一个固定位置的一行的次数。
Handler_update 请求更新表中一行的次数。
Handler_write 请求向表中插入一行的次数。
Key_blocks_used 用于关键字缓存的块的数量。
Key_read_requests 请求从缓存读入一个键值的次数。
Key_reads 从磁盘物理读入一个键值的次数。
Key_write_requests 请求将一个关键字块写入缓存次数。
Key_writes 将一个键值块物理写入磁盘的次数。
Max_used_connections 同时使用的连接的最大数目。
Not_flushed_key_blocks 在键缓存中已经改变但是还没被清空到磁盘上的键块。
Not_flushed_delayed_rows 在INSERT DELAY队列中等待写入的行的数量。
Open_tables 打开表的数量。
Open_files 打开文件的数量。
Open_streams 打开流的数量(主要用于日志记载)
Opened_tables 已经打开的表的数量。
Questions 发往服务器的查询的数量。
Slow_queries 要花超过long_query_time时间的查询数量。
Threads_connected 当前打开的连接的数量。
Threads_running 不在睡眠的线程数量。
Uptime 服务器工作了多少秒。

传送门:
生产稳定:JVM调优- java进程,JVM频繁GC,导致CPU占用、内存占用过高过高定位和排查
学习提高:Idea 使用docker 部署SpringBoot应用并指定JVM参数,jdk8版本
-------------欢迎各位留言交流,如有不正确的地方,请予以指正。【Q:981233589】

你可能感兴趣的:(性能优化,Java常见面试题,微服务架构,mysql,tomcat,java)