监控告警:目前采用的是单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,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个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:Metaspacesize为21810376B(大约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为)。
● 吞吐量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秒内存。
未调整任何参数的情况:
参数调整之后,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]
目前分析占据内存最多的几块
● 页面小红点对应的接口功能,由前端轮询调用,导致内存增加过多;(这个可以改由websocket提供)
● 业务程序和异步定时job共存,xxl的许多job会开启新线程处理,导致内存增加;(这个可以拆分服务)
● 程序内存采用的注册中心常驻内存,这块正常,不需要处理;(只需要对相关参数微调,比如心跳频率和间隔)
目前这块需要监测到OOM的发生才方便定位错误代码。
OOM 堆转储:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/heapdump.hprof
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/heapdump.hprof
OOM后立即执行备份方案:
-XX:OnOutOfMemoryError='sh /usr/shellfile/xxxxx.sh
jinfo 是 JDK 自带的命令,可以用来查看正在运行的 java 应用程序的扩展参数,包括Java System属性和JVM命令行参数;也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息
使用 jinfo 可以在不重启虚拟机的情况下,可以动态的修改 jvm 的参数。尤其在线上的环境特别有用。
命令:jinfo -flag [+|-]name pid
描述:开启或者关闭对应名称的参数
jinfo pid
jinfo -flag MaxHeapSize pid
jinfo -flag +HeapDumpOnOutOfMemoryError pid
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:
JVM内存占物理内存比例 50% - 80%
-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 逃逸分析 标量替换 ...
这些不建议设置
-XX:SurvivorRatio
-XX:PreTenureSizeThreshold
大对象到底多大
-XX:MaxTenuringThreshold
-XX:+ParallelGCThreads
并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
-XX:+UseAdaptiveSizePolicy
自动选择各区大小比例
-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会尝试用各种手段达到这个时间,比如减小年轻代
-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的堆空间占用比例
————————————————————————————————
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、长连接,不用轮训
————————————————————————————————
慢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】