JVM调优
2021-05-20 18:25:51 0 举报
AI智能生成
jvm调优笔记
作者其他创作
大纲/内容
基础及分析
为什么要JVM调优?
保证服务的稳定
复杂和高并发下的服务,必须保证每次gc不会出现性能下降,
各种性能指标不会出现波动,gc回收规律而且干净,找到合
适的jvm设置
各种性能指标不会出现波动,gc回收规律而且干净,找到合
适的jvm设置
JVM调优目标:使用较小的内存占用来获得较高的吞吐量或者较低的延迟。
调优指标
内存占用:程序正常运行需要的内存大小。
延迟:由于垃圾收集而引起的程序停顿时间。
吞吐量:用户程序运行时间占用户程序和垃圾收集占用总时间的比值。
调优阶段
上线前:根据需求(支撑多少QPS)进行JVM规划和预估调优(几台机器,多大内存,堆内存划分)
上线初期:根据日志优化JVM运行环境(解决慢、卡顿问题)
上线后期:解决JVM运行过程中出现的各种问题(OOM,逃逸分析,频繁full gc)
JVM运行参数
标准参数,-开头
通过 java -help 命令查看
常用命令
-version 输出版本
-showversion 输出产品版本并继续
-D<name>=<value> 设定参数
例如 java -Dstr=123 TestJVM
例如 java -Dstr=123 TestJVM
-server JVM类型
服务器上JVM默认启动server JVM。初始堆空间大,使用的是并行垃圾回收器,启动慢运行快。
-client JVM类型
初始堆空间小,使用串行的垃圾回收器,启动快,运行相对慢
非标准参数(java -X)
-X开头
通过java -X 命令查看
常用命令
运行模式
-Xint 解释模式
-Xcomp 编译模式
-Xmixed 混合模式(前两者混合使用,jvm默认使用)
-Xms100m -Xmx100M:最小堆和最大堆
-Xmn2G:年轻代(新生代)
整个堆大小=年轻代大小 + 年老代大小 (持久代不算入).
持久代一般固定大小为64m,所以增大年轻代后,将会减小
年老代大小.此值对系统性能影响较大,Sun官方推荐配置
为整个堆的3/8
持久代一般固定大小为64m,所以增大年轻代后,将会减小
年老代大小.此值对系统性能影响较大,Sun官方推荐配置
为整个堆的3/8
-Xss1m:线程堆栈(栈空间)大小
-XX开头
通过java -XX:+PrintFlagsFinal -version | more 命令查看
常用参数
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintHeapAtGC
打印GC前后的详细堆栈信息
-XX:NewRatio=4
置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代).设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4
设置年轻代中Eden区与Survivor区的大小比值.设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m
设置持久代大小为16m
-XX:MaxTenuringThreshold=0
设置垃圾最大年龄.如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代.
对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象
会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代
即被回收的概论.
对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象
会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代
即被回收的概论.
-XX:+DisableExplicitGC
禁用手动调用gc
-XX:+UseParallelGC
并行收集器
并行收集参数
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数.并行收集线程数.
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比.公式为1/(1+n)
查看java进程及运行参数
jps -lv 查看进程
jinfo -flags [pid] 查看进程运行参数
JVM内存模型(堆内存)
原图地址:
参考资料1:
参考资料2:
堆
Eden区:From区:To区域的比例是8:1:1
Eden区:From区:To区域的比例是8:1:1
jdk1.8的内存模型=年轻代(Eden + 2*Survivor) + 年老代(OldGen)
1.7与1.8最大区别:元数据区取代了永久代;
元空间与永久代最大区别:元数据空间并不在虚拟机中,而是使用本地内存
元空间与永久代最大区别:元数据空间并不在虚拟机中,而是使用本地内存
JVM分析工具
jstat 命令查看堆内存
jstat -class pid 类加载统计
Loaded:加载class的数量
Bytes:所占用空间大小
Unloaded:未加载数量
Bytes:未加载占用空间
Time:时间
Bytes:所占用空间大小
Unloaded:未加载数量
Bytes:未加载占用空间
Time:时间
jstat -compiler pid JIT编译统计
Compiled:编译数量。
Failed:失败数量
Invalid:不可用数量
Time:时间
FailedType:失败类型
FailedMethod:失败的方法
Failed:失败数量
Invalid:不可用数量
Time:时间
FailedType:失败类型
FailedMethod:失败的方法
jstat -gc pid 垃圾回收统计
S0C:第一个Survivor区的大小(KB)
S1C:第二个Survivor区的大小(KB)
S0U:第一个Survivor区的使用大小(KB)
S1U:第二个Survivor区的使用大小(KB)
EC:Eden区的大小(KB)
EU:Eden区的使用大小(KB)
OC:Old区大小(KB)
OU:Old使用大小(KB)
MC:方法区大小(KB)
MU:方法区使用大小(KB)
CCSC:压缩类空间大小(KB)
CCSU:压缩类空间使用大小(KB)
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
S1C:第二个Survivor区的大小(KB)
S0U:第一个Survivor区的使用大小(KB)
S1U:第二个Survivor区的使用大小(KB)
EC:Eden区的大小(KB)
EU:Eden区的使用大小(KB)
OC:Old区大小(KB)
OU:Old使用大小(KB)
MC:方法区大小(KB)
MU:方法区使用大小(KB)
CCSC:压缩类空间大小(KB)
CCSU:压缩类空间使用大小(KB)
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
jmap 命令(内存汇总、内存溢出定位分析)
jmap -heap 6219
#查看内存使用情况
Heap Configuration: #堆内存配置信息
Heap Usage: # 堆内存的使用情况
Heap Configuration: #堆内存配置信息
Heap Usage: # 堆内存的使用情况
jmap -histo <pid> | more
#查看内存中对象数量及大小
#对象说明
B byte
C char
D double
F float
I int
J long
Z boolean
[ 数组,如[I表示int[]
[L+类名 其他对象
#对象说明
B byte
C char
D double
F float
I int
J long
Z boolean
[ 数组,如[I表示int[]
[L+类名 其他对象
jmap -dump:format=b,file=dumpFileName <pid>
#将内存使用情况dump到文件中
#示例
jmap -dump:format=b,file=/tmp/dump.dat 6219
#示例
jmap -dump:format=b,file=/tmp/dump.dat 6219
jhat -port <port> <file>
#通过jhat对dump文件进行分析
#示例
步骤1: jhat -port 9999 /tmp/dump.dat
步骤2: 打开浏览器进行访问:http://192.168.40.133:9999/
#示例
步骤1: jhat -port 9999 /tmp/dump.dat
步骤2: 打开浏览器进行访问:http://192.168.40.133:9999/
jmap + MAT工具定位分析
介绍:MAT(Memory Analyzer Tool),基础Eclipse快速、多功能的内存分析工具
MAT网盘下载:
操作:解压->启动MemoryAnalyzer.exe ->导入dump文件
jstack 查看线程执行情况
jstack <pid>
错误截图
线程生命周期(五个阶段):新建、就绪、运行、阻塞、销毁
VisualJVM工具
作用
启动文件位置:jdk/bin/jvisualvm.exe
远程监控tomcat配置
#在tomcat的bin目录下,修改catalina.sh,添加如下的参数
JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 - Dcom.sun.management.jmxremote.authenticate=false - Dcom.sun.management.jmxremote.ssl=false"
#这几个参数的意思是:
#-Dcom.sun.management.jmxremote :允许使用JMX远程管理
#-Dcom.sun.management.jmxremote.port=9999 :JMX远程连接端口
#-Dcom.sun.management.jmxremote.authenticate=false :不进行身份认证,任何用户都可以连接
#-Dcom.sun.management.jmxremote.ssl=false :不使用ssl
JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 - Dcom.sun.management.jmxremote.authenticate=false - Dcom.sun.management.jmxremote.ssl=false"
#这几个参数的意思是:
#-Dcom.sun.management.jmxremote :允许使用JMX远程管理
#-Dcom.sun.management.jmxremote.port=9999 :JMX远程连接端口
#-Dcom.sun.management.jmxremote.authenticate=false :不进行身份认证,任何用户都可以连接
#-Dcom.sun.management.jmxremote.ssl=false :不使用ssl
垃圾回收
垃圾收集算法
引用计数法
介绍
在对象中添加一个引用计数器,每当一个地方引用它是,计数器值就+1;
当引用失效是,计数器就-1;任何时刻计数器为0的对象就是可回收的
当引用失效是,计数器就-1;任何时刻计数器为0的对象就是可回收的
优点
实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。
在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报outofmember 错误。
区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。
在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报outofmember 错误。
区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。
缺点
每次对象被引用时,都需要去更新计数器,有一点时间开销。
浪费CPU资源,即使内存够用,仍然在运行时进行计数器的统计。
无法解决循环引用问题。(最大的缺点)
浪费CPU资源,即使内存够用,仍然在运行时进行计数器的统计。
无法解决循环引用问题。(最大的缺点)
标记清除法
介绍
此算法执行分两个阶段:
阶段1从引用根节点开始标记所有被引用的对象;
阶段2遍历整个堆,把未标记的对象清除。
阶段1从引用根节点开始标记所有被引用的对象;
阶段2遍历整个堆,把未标记的对象清除。
优点
解决引用计数法循环引用的问题
缺点
a. 会产生碎片;
b. 标记、清除需遍历2次,效率低
c. GC时,停止应用程序线程,不合适交互性要求高的应用
b. 标记、清除需遍历2次,效率低
c. GC时,停止应用程序线程,不合适交互性要求高的应用
标记压缩法
介绍
基于标记清除法之上优化,解决了碎片化问题。标记阶段相同,
清除阶段是将存活对象压缩在内存一端,然后清理边界外垃圾。
清除阶段是将存活对象压缩在内存一端,然后清理边界外垃圾。
优点
解决内存碎片化
缺点
相对标记清除法多了一步压缩移动的步骤,性能会有一定影响
复制算法
介绍
此算法把内存空间划分为两个相等的区域,from和to域,每次只使用from域,to域则空闲。
当from域内存不够,执行GC操作,遍历当前from域,把存活对象复制到to域,然后将from域
内存空间清空,并交换两个内存域的角色
当from域内存不够,执行GC操作,遍历当前from域,把存活对象复制到to域,然后将from域
内存空间清空,并交换两个内存域的角色
优点
在垃圾对象多的情况下,效率较高。清理后,内存无碎片
缺点
分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低
新生代中的可用内存:复制算法用来担保的内存为9:1
可用内存中Eden:S1区为8:1
即新生代中Eden:S1:S2 = 8:1:1
可用内存中Eden:S1区为8:1
即新生代中Eden:S1:S2 = 8:1:1
分代算法
介绍
根据回收对象的特点进行选择上述算法
新生代
复制算法
一个Eden区、两个Survival区(From Survival、To Survival) (8:1:1)
大多说情况下对象在Eden区中分配,当Eden区没有足够的空间时,发起一次Minor GC
内存分配
回收策略
回收策略
大对象直接进入老年代
为了降低大对象分配内存时造成的内存复制的开销
长期存活的对象进入老年代
对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁
动态对象年龄判断
Minor GC之后,如果检测到可能长期存活的对象,则让其尽早进入老年代
老年代空间分配担保机制
Minor GC之前做风险判断,是否允许担保失败,如不允许则改为Full GC
老年代
标记清除/标记压缩
Major GC
整个Java堆
方法区/永久代/元空间
对永久代的回收主要包括废弃的常量和无用的类
永久代和元空间的区别在于永久代位于JVM的方法区中,元空间并不在虚拟机内存中,而是本地内存
Full GC
垃圾收集器
分代模型
新生代
Serial
简单高效,单线程,收集时暂停其他所有线程(触发STW)
ParNew
Serial的多线程版本,是首选新生代收集器,可配合CMS
Parallel Scavenge
以吞吐量为优先的多线程收集器,不支持CMS
老年代
Serial Old
Serial的老年代版本,可以作为CMS的后备预案
Parallel Old
Parallel Scavenge的老年代版本,多线程,吞吐量优先
CMS
并发收集、低停顿
适用于当今互联网网站或基于浏览器的B/S系统的服务器上,这类应用通常
都较关注服务的响应速度,尽量缩短系统的停顿时间,给用户更好的交互体验
都较关注服务的响应速度,尽量缩短系统的停顿时间,给用户更好的交互体验
流程
初始标记:标记GC Roots能直接关联到的对象,速度很快,STW
并发标记:从GC Roots的直接关联对象开始遍历整个对象图,耗时长
重新标记:修正并发标记期间可能造成的改动,相比初始标记STW略长
并发清除:清除标记阶段已经死亡的对象,此阶段和用户线程并发工作
缺点
对CPU敏感,并发阶段虽然不会导致用户线程停顿,但却由于占用资源导致应用程序变慢,降低总吞吐量
由于CMS在清理时与用户线程并发,运行期间还伴随有新垃圾产生,有可能触发担保机制而产生较大停顿
基于标记清除算法,造成大量的空间碎片,导致没有足够的连续空间存放大对象,不得不提前触发Full GC
组合使用
Parallel Scavenge + Parallel Old (JDK1.8默认)
分区模型
G1垃圾收集器(后期还需补充)
#启动参数
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails -Xmx256m
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails -Xmx256m
G1中提供了三种模式垃圾回收模式,在不同的条件下被触发。
Young GC
触发条件:
Eden空间耗尽时会被触发。
回收区域:对Eden区进行GC。
Eden空间耗尽时会被触发。
回收区域:对Eden区进行GC。
Remembered Set(已记忆集合)
作用是跟踪指向某个堆内的对象引用。
Mixed GC
触发条件:
由参数 -XX:InitiatingHeapOccupancyPercent=n 决定。
默认:45%。
该参数的意思是:当老年代大小占整个堆大小百分比达到该阀值时触发。
回收区域:年轻代+部分老年代。
由参数 -XX:InitiatingHeapOccupancyPercent=n 决定。
默认:45%。
该参数的意思是:当老年代大小占整个堆大小百分比达到该阀值时触发。
回收区域:年轻代+部分老年代。
GC步骤
1. 全局并发标记(global concurrent marking)
2. 拷贝存活对象(evacuation)
Full GC
回收区域:针对全量堆内存
G1优化建议
年轻代大小
避免使用 -Xmn 选项或 -XX:NewRatio 等其他相关选项显式设置年轻代大小。
固定年轻代的大小会覆盖暂停时间目标。
暂停时间目标不要太过严苛
G1 GC 的吞吐量目标是 90% 的应用程序时间和 10%的垃圾回收时间
其他收集器
Epsilon
PGC、C4、OpenJ9
查看JVM使用的垃圾收集器
jmap -heap <PID>
java -XX:+PrintFlagsFinal -version | grep :
java -XX:+PrintCommandLineFlags -version
垃圾收集日志
日志级别从低到高
Trace、Debug、Info、Warning、Error、Off ;默认为Info
命令
查看GC基本信息
-XX:printGC
-Xlog:gc JDK1.9之后使用
查看GC详细信息
-XX:+PrintGCDetails
-Xlog:gc* JDK1.9之后使用
查看GC前后 堆和方法区 可用容量变化
-XX:+PrintHeapAtGC
-Xlog:gc+heap=debug
查看GC过程中用户线程并发时间及停顿的时间
-XX:+PrintGCApplicationConcurrentTime
-XX:+PrintGCApplicationStoppedTime
-Xlog:safepoint
查看熬过收集器后剩余对象的年龄分布信息
-XX:+PrintTenuring-Distribution
-Xlog:gc_age=trace
#示例:
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xmx256m -XX:+PrintGCDetails - XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC - Xloggc:F://test//gc.log
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xmx256m -XX:+PrintGCDetails - XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC - Xloggc:F://test//gc.log
GC Easy 可视化工具
其它优化
Tomcat8的优化
看懂Java底层字节码
编码的优化建议
0 条评论
下一页