JVM参数
JVM堆内存的分配
-Xmx:512
等价-XX:MaxHeapSize设置最大堆内存为 512 M;【默认是物理内存的1/4】
-Xms:215
等价-XX:InitialHeapSize 初始堆内存为 215 M;【默认初始化为物理内存1/64】
-Xmn2G
设置年轻代大小为2G
-XX:SurvivorRatio
设置新生代 Eden、Form Survivor、To Survivor 占比。【默认8:1:1】
survivor 大了,会浪费空间,空间利用率低。若 survivor 太小,会使一些大对象在 minor gc
时直接从 eden 区到 old 区,让 old 区的 gc 频繁 【JVM调优策略】
-XX:NewRatio
设置分代垃圾回收器新生代和老生代内存占比;【默认1:2】
java
-Xmx10240m -Xms10240m -Xmn5120m -XXSurvivorRatio=3
-Xmx10240m:代表最大堆 -Xms10240m:代表最小堆 -Xmn5120m:代表新生代 -XXSurvivorRatio=3:代表Eden:Survivor = 3 根据Generation-Collection算法(目前大部分JVM采用的算法),一般根据对象的生存周期将堆内存分为若干不同的区域,一般情况将新生代分为Eden
,两块Survivor; 计算Survivor大小, Eden:Survivor = 3,总大小为5120,3x+x+x=5120
x=1024 新生代大部分要回收,采用Copying算法,快! 老年代 大部分不需要回收,采用Mark-Compact算法
堆内存上对象的分配与回收
-XX:MaxNewSize
设置最大年轻区内存;
-XX:MaxTenuringThreshold=5
设置新生代对象经过 5 次 GC 晋升到老年代;【默认值15】
-XX:PretrnureSizeThreshold
设置大对象的值,超过这个值的大对象直接进入老生代;
-XX:HandlePromotionFailure
是否允许担保失败
JVM方法区的分配
-XX:PermSize
设置非堆内存初始化值,【默认是物理内存的1/64】
-XX:MaxPermSize
设置最大非堆内存(方法区)内存大小 【默认是物理内存的1/4】
-XX:UserTLAB
是否启用TLAB空间分配。
方法区(元空间)参数设置
-XX:MetaspaceSize
指定元空间大小
-XX:MaxMetaspaceSize
指定元空间最大空间
基本信息修改
-XX:+PrintGCDetails
打印GC收集信息
-XX:+UseSerialGC
是否使用串行垃圾收集器
JVM默认值
-XX:+PrintFlagsInitial
查看初始默认值
-XX:+PrintFlagsFinal
查看JVM默认配置值
Java栈
-Xss
等价于-XX:ThreadStackSize设置单个线程的大小,默认512KB
直接物理内存参数
-XX:MaxDirectMemorySize
设置最大物理内存
X参数
-Xint
解释执行
-Xcomp
第一次使用就编译成本地代码
-Xmixed
混合模式,JVM自己来决定是否编译成本地代码
查看JVM运行时参数
java
-XX:+PrintFlagsInitial
java
-XX:+PrintFlagsFinal
设置垃圾回收器
-XX:+UseSerialGC
使用SerialGC垃圾回收器
-XX:+UseParallelGC
使用parallel垃圾回收器
-XX:+UseConcMarkSweepGC
使用CMS垃圾回收器
-XX:+UseG1GC
使用 G1 垃圾收集器
-XX:MaxGCPauseMillis=
设置期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到),默认值是 200 毫秒。
-XX:G1HeapRegionSize=n
设置的 G1 区域的大小。值是 2 的幂,范围是 1 MB 到 32 MB 之间。
目标是根据最小的 Java 堆大小划分出约 2048 个区域。 默认是堆内存的1/2000。
-XX:ParallelGCThreads=n
设置并行垃圾回收线程数,一般将n的值设置为逻辑处理器的数量,建议最多为8。
-XX:ConcGCThreads=n
设置并行标记的线程数。将n设置为ParallelGCThreads的1/4左右。 -XX:InitiatingHeapOccupancyPercent=n
设置触发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45%。
JVM优化
JVM性能指标
调优的最终目的都是为了应用程序使用最小的硬件消耗来承载更大的吞吐量。JVM调优主要是针对垃圾收集器的收集性能进行优化令运行在虚拟机上的应用,能够使用更少的内存(Footprint),及更低的延迟(Latency),获取更大的吞吐量(Throughput)
什么时候JVM调优?
遇到以下情况,就需要考虑进行JVM调优:
- 系统吞吐量下降与响应延迟(P99);
- Heap内存(老年代)持续上涨至出现OOM; 3. Full GC 次数频繁;
- GC 停顿过长(超过1秒);
- 应用出现OutOfMemory 等内存异常;
- 应用中有使用本地缓存且占用大量内存空间;
调优调什么?
内存分配 + 垃圾回收!
- 合理使用堆内存
- GC高效回收占用的内存的垃圾对象 3. GC高效释放掉内存空间
JVM运行情况预估
用 jstat gc -pid
命令可以计算出如下一些关键数据,有了这些数据就可以采用之前介绍过的优化思路,先给自己的系统设置一些初始性的 JVM参数,比如堆内存大小,年轻代大小,Eden和Survivor的比例,老年代的大小,大对象的阈值,大龄对象进入老年代的阈值等。
年轻代对象增长的速率
可以执行命令 jstat -gc pid 1000 10
(每隔1秒执行1次命令,共执行10次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象,如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。注意,一般系统可能有高峰期和日常期,所以需要在不同的时间分别估算不同情况下对象增长速率。
Young GC的触发频率和每次耗时
知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次,Young GC的平均耗时可以通过 YGCT/YGC 公式算出,根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久。
每次Young GC后有多少对象存活和进入老年代
这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden, survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次 Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。
Full GC的触发频率和每次耗时
知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。
优化思路其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。
JVM 性能优化常见问题
一类是OOM,这个之前的一篇文章介绍过,不在详细讲解
另外两类就是频繁的GC、以及GC的时间过长。
频繁GC
频繁GC 其实包括Young GC和Full GC,前者一般是几毫秒或几十毫秒,后者是几十毫秒到几百毫秒。所以要减少Full GC的频次,对于Young GC来说,一般只需要增大年轻代的大小,大多数对象都会几次GC后死亡。因为Full GC频繁,比较消耗CPU资源和STW资源时间较长,所以在发生频繁Full GC时,一般CPU利用率会飙升。程序处理慢,接口相应时间超时等。
触发Full GC的主要时老年代空间不足。老年代对象一般是大对象、长期存活的对象、空间分配担保
长期存活对象
大多数应用程序不会存在长期存活的对象,所以一般内存泄露才是导致对象长期存活无法回收的原因,Full GC 回收率低 释放空间少,说明是存在内存泄漏的。频繁Full GC之后,会出现OOM。
大对象
比如说一次性加载过多的数据,直接加载到内存中,当接口被大量调用,大量大对象会被创建,从而导致老年代空间不足,引发频繁的Full GC。一般这种情况都需要在FullGC前,添加 JVM参数
headDumpBeoferFullGC dump内存快照,分析占用堆内存比较多的那个对象。
空间分配担保
Young GC时,如果S区空间不足,JVM会触发空间分配担保,将对象直接存储到老年代,如果每次Young GC,S区都占满,那么需要增大To Survivor区,避免空间分配担保,减少进入老年代的对象数量。
GC时间过长
GC时间过长的常见原因有:堆内存过大、Concurrent Mode Failure、操作系统swap
堆内存过大
对于年轻代使用的是标记-复制算法,增大空间不会导致存活对象增多,但是老年代使用的是标记-整理=标记清除算法,空间增大会导致存辉对象增多,因此Full GC时间跟老年代的大小有关,老年代过大会导致GC时间长。对于空间比较大的堆内存,可以选择GC时间可控的G1垃圾回收器。
Concurrent Mode Failure
CMS使用并发垃圾回收算法,应用程序和JVM垃圾回收器线程并行执行,一般在GC前会预留一定的堆内存空间,如果预留的堆内存不足,应用程序无法执行,JVM会抛出Concurrent Mode Failure错误,并且暂停CMS的执行,切换到Serial Old垃圾回收器。出现 Concurrent MOde Failure 需要减少预留的空间大小
操作系统SWAP
如果物理内存是8G,但是分配了JVM 10G,操作系统会将内存不活跃的数据放入磁盘,因为swap设计磁盘的IO操作,所以时间比较长,所以需要在设置的时候保证不要超过操作系统内存大小,比如操作系统8G,那么JVM设置为4G。
其他
https://juejin.cn/post/7205141492264976445 一次线上OOM问题分析
https://heapdump.cn/article/3489050 实战案例:记一次dump文件分析历程
https://www.cnblogs.com/mylibs/p/production-accident-0002.html
https://juejin.cn/post/7078624931826794503
https://heapdump.cn/article/1661497
https://shuyi.tech/archives/have-a-try-in-jvm-combat
https://mp.weixin.qq.com/s/df1uxHWUXzhErxW1sZ6OvQ