欢迎访问我的博客,你的支持,是我最大的动力!

Java应用性能监控与调优(二)JVM_GC调优

Linux 小马奔腾 61℃ 评论
目录:
[显示]
JVM层GC调优
JVM的内存结构

运行时数据区(规范)

程序计数器 PC Register,存放当前正在执行的指令的地址
虚拟机栈 JVM Stacks,线程私有,生命周期与线程相同,线程堆栈,发生异常打印出来的堆栈
堆 Heap,被所有线程共享的一块内存区域,存放对象实例
方法区 Method Area,各线程共享的内存区域,存储加载的类、常量、静态变量、编译代码等 Non-Heap
常量池 Run-Time Constant Pool,属于方法区,存放编译期生成的字面量和符号引用
本地方法栈 Native Method Stacks,为Native方法服务

Metaspace,存放class、package、method、field、字节码、常量池、符号引用等
CCS,存放32位指针的class  -> -XX:UseCompressedClassPointers
CodeCache,存放经JIT编译后的本地代码、JNI使用的C代码

常用参数:
-Xms -Xmx  最小/大堆内存大小
-XX:NewSize  -XX:MaxNewSize   新生代内存大小
-XX:NewRatio  新生代与老年代内存比例
-XX:SurvivorRatio  Even区与Survivor区内存比例
-XX:MetaspaceSize -XX:MaxMetaspaceSize   非堆区内存大小   非堆区通常仅需调整该值
-XX:UseCompressedClassPointers  启用压缩类指针  默认最大1G
-XX:CompressedClassSpaceSize 压缩类空间大小
-XX:InitialCodeCacheSize   CodeCache初始大小
-XX:ReservedCodeCacheSize  CodeCache最大大小

垃圾回收算法

枚举根节点,做可达性分析
根节点:类加载器、Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等

1、标记清除算法
分为标记和清除两阶段,先标记出要回收的对象,在标记完成后统一回收
缺点:效率不高,产生碎片(碎片太多会导致提前GC)

2、复制算法
将可用内存划分为大小相等两块,每次只使用其中一块,当这块内存用完,不将还存活的对象复制到另一块,并把该块清空
优缺点:实现简单、运行高效,但空间利用率低

3、标记整理算法
标记过程和标记清除一样,但在清理后,会对存活对象做移动,减少碎片
优缺点:没有内存碎片,但整理内存比较耗时

分带垃圾回收
young区用复制算法
old区用标记清除或标记整理

对象分配
对象优先在eden区分配
大对象直接进入老年代     ->  -XX:PretenureSizeThreshold (对象大小超过该值后,直接进入old区)
长期存活对象进入老年代  -> -XX:MaxTenuringThreshold (每经过一次YGC,年龄加1,当年龄达到设置的值后,移动到old区)
-XX:+PrintTenuringDistribution (YGC后,打印年龄分布情况)
-XX:TargetSurvivorRatio (S区回收后存活对象比例达到设置的值后,计算存活对象平均年龄,平均年龄与最大值之间取最小值,当对象年龄超过该值会被移动到old区)

垃圾收集器

串行收集器 Serial: Serial、Serial Old  <- 单线程操作
并行收集器 Parallel: Parallel Scavenge、Parallel Old  <- 吞吐量优先
并发收集器 Concurrent: CMS、G1 <- 停顿时间优先

并行 Paralle,多条垃圾收集线程并行工作,但用户线程仍然处于等待状态,适用于科学计算、后台处理等弱交互场景
并发 Concurrent,用户线程与垃圾收集线程同时执行(不一定是并行,可能交替执行),垃圾收集线程在执行时不会停顿用户程序的运行。
适用于对响应时间有要求的场景,如web应用

评价指标:

停顿时间,垃圾收集器做垃圾回收中断应用执行的时间  -> -XX:MaxGCPauseMillis
吞吐量,花在垃圾收集的时间和花在应用的时间的占比  -> -XX:GCTimeRatio=<n>  垃圾收集时间占: 1/(1+n)

1、串行收集器
-XX:+UseSerialGC Y区 -XX:+UseSerialOldGC O区

2、并行收集器
吞吐量优先
-XX:+UseParallelGC Y区 -XX:+UseParallelOldGC O区
server模式下的默认收集器  (内存大于2G,双核即判断为server模式)

3、并发收集器
响应时间优先
CMS: -XX:+UseConcMarkSweepGC O区 -XX:+UseParNewGC Y区
G1: -XX:+UseG1GC

tomcat中的gc选项

CMS_OPTION:
-XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5

G1_OPTION:
-XX:+UseG1GC -XX:+UseStringDeduplication -XX:StringDeduplicationAgeThreshold=3 -XX:+UseCompressedClassPointers -XX:MaxGCPauseMillis=200

jdk8中推荐使用G1垃圾收集器

如何选择垃圾收集器
1、优先调整堆的大小让服务器自己来选择
2、如果内存小于100M,使用串行收集器
3、如果是单核,并且没有停顿时间的要求,串行或JVM自己选
4、如果允许停顿时间超过1秒,选择并行或JVM自己选
5、如果响应时间最重要,并且不能超过1秒,使用并发收集器

Parallel Collector 并行

-XX:+UseParallelGC 手动开启,server默认开启
-XX:ParallelGCThreads=<n>  使用多少个GC线程  CPU>8 n=5/8 ; CPU<8 n=CPU

自适应<-动态内存调整
-XX:MaxGCPauseMillis=<n>
-XX:GCTimeRatio=<n>
-Xmx<n>

动态内存调整
-XX:YoungGenerationSizeIncrement=<y>   young区,默认20%
-XX:TenuredGenerationSizeIncrement=<t>  old区,默认20%
-XX:AdaptiveSizeDecrementScaleFactor=<d>  减少,默认4%

CMS Collector 并发

并发收集,低停顿,低延迟,老年代收集器

CMS垃圾收集过程:
1、CMS initial mark,初始标记Root,STW(stop the world)
2、CMS concurrent mark,并发标记
3、CMS-concurrent-preclean,并发预清理
4、CMS remark,重新标记,STW
5、CMS concurrent sweep,并发清除
6、CMS-concurrent-reset,并发重置

CMS缺点
CPU敏感,会占用一个cpu,应用程序只能使用剩下的cpu
浮动垃圾
空间碎片

CMS相关参数
-XX:ConcGCThreads 并发的GC线程数
-XX:+UseCMSCompactAtFullCollection  FullGC之后做压缩,减少内存碎片
-XX:CMSFullGCsBeforeCompaction  多少次FullGC之后压缩一次
-XX:CMSInitiatingOccupancyFraction old区填满多少后,触发FullGC,默认92,即92%
-XX:+UseCMSInitiatingOccupancyOnly  是否动态可调
-XX:+CMSScavengeBeforeRemark  FullGC之前先做YGC
-XX:+CMSClassUnloadingEnabled 启用回收Perm区(jdk7之前)

iCMS  增量CMS
适用于单核或双核  (jdk8中已废弃)

G1 Collector 并发(推荐)

新生代和老生代收集器,大内存(>6GB),停顿时间短(<0.5s),jdk9中的默认垃圾收集器
没有新生代、老生代概念,将内存分为小块(Region)

SATB,GC开始时存活对象的快照
RSet,记录其他Region中对象引用本Region中对象的关系,谁引用了我的对象

YoungGC
1、新对象进入Eden区
2、存活对象拷贝到Survivor区
3、存活时间达到年龄阈值时,对象晋升到Old区

MixedGC
1、不是FullGC,回收所有的Young和部分Old
2、global concurrent marking  全局并发标记

global concurrent marking  全局并发标记
1、Initial marking phase,标记GC Root,STW
2、Root region scanning phase,标记存活Region
3、Concurrent marking phase,标记存活对象
4、Remark phase,重新标记,STW
5、Cleanup phase,部分STW

MixedGC时机
InitiatingHeapOccupancyPercent,堆占有率达到这个数值则触发global concurrent marking,默认45%
G1HeapWastePercent,在每次YGC之后和再次发生Mixed GC前,会检查垃圾占比是否达到此参数,达到了才会发生Mixed GC

MixedGC相关参数
G1MixedGCLiveThresholdPercent,Old区和region被回收时的存活对象占比
G1MixedGCCountTarget,一次global concurrent marking后,最多执行Mixed GC的次数
G1OldCSetRegionThresholdPercent,一次Mixed GC中能被选入CSet的最多old区的region数量(CSet存入要回收的region)

常用参数
-XX:+UseG1GC 开启G1
-XX:G1HeapRegionSize=<n> region的大小,1-32M,最多2048个region
-XX:MaxGCPauseMillis=200 最大停顿时间
-XX:G1NewSizePercent -XX:G1MaxNewSizePercent  young区大小
-XX:G1ReservePercent=10 保留防止to space溢出(S0<->S1)
-XX:ParallelGCThreads=<n> SWT线程数
-XX:ConcGCThreads=<n> 并发线程数=0.25*并行

最佳实践
年经代大小:避免使用-Xmn -XX:NewRatio等显式设置young区大小,会覆盖暂停时间目标
暂停时间目标:暂停时间不要太严苛,其吞吐量目标是90%的应用程序时间和10%的垃圾回收时间,太严苛会直接影响到吞吐量
MixGC调优:
-XX:InitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent
-XX:G1HeapWastePercent
-XX:G1MixedGCCountTarget
-XX:G1OldCSetRegionThresholdPercent

是否要切换到G1
50%以上的堆被存活对象占用
对象分配和晋升的速度变化非常大
垃圾回收时间特别长,超过了1秒

可视化GC日志分析工具

日志打印及格式

打印日志相关参数
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps 时间 -XX:+PrintGCDateStamps 时间 -Xloggc:$CATALINA_HOME/logs/gc.log -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution 年龄信息

ParallelGC日志格式
日志文件中会显示GC相关的命令行参数,包括默认的和自动设置的
gc原因
young区回收前大小,回收后大小 (总大小),gc耗时
堆回收前大小,回收后大小 (总大小),gc耗时

CMS日志格式
YGC和ParallelGC差不多
FGC会记录CMS各个阶段

G1日志格式
会记录各个阶段情况
区分为young和mixed两种gc

在线工具 https://gceasy.io/

地址:https://gceasy.io/
上传gc log文件
生成报告

关键指标:吞吐量 throuthput  停顿时间 latency

GCViewer

项目地址:https://github.com/chewiebug/GCViewer

也是解析gc log日志文件

Tomcat的GC调优

GC调优步骤
1、打印GC日志
2、根据日志得到关键性能指标
3、分析GC原因,调优JVM参数

初始设置:
-XX:+DisableExplicitGC 禁用明确的GC,代码中的手动GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_HOME/logs/ -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log

常用参数:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=$CATALINA_HOME/logs
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps
-XX:+DisableExplicitGC
-XX:+UseCompressedOops
-XX:+UseCompressedClassPointers

Parallel GC调优指导原则
除非确定,否则不要设置最大堆内存
优先设置吞吐量目标
如果吞吐量目标达不到,调大最大内存,不能让OS使用Swap,如果仍然达不到,降低目标
吞吐量能达到,GC时间太长,设置停顿时间的目标

ParallelGC调优
设置Metaspace大小
-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M
添加吞吐量和停顿时间参数
-XX:GCTimeRatio=99 -XX:MaxGCPauseMillis=100
修改动态扩容增量
-XX:YoungGenerationSizeIncrement=30

G1 GC最佳实践
年轻代大小:避免使用-Xmn -XX:NewRatio等显式设置young区大小,会覆盖暂停时间目标
暂停时间目标:暂停时间不要太严苛,其吞吐量目标是90%的应用程序时间和10%的垃圾回收时间,太严苛会直接影响到吞吐量
关于MixGC调优:
-XX:InitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent
-XX:G1HeapWastePercent
-XX:G1MixedGCCountTarget
-XX:G1OldCSetRegionThresholdPercent

G1 GC调优
G1调优相关参数:
-XX:+UseG1GC -Xms128M -Xmx128M -XX:MetaspaceSize=64M -XX:MaxGCPauseMillis=100 -XX:+UseStringDeduplication 字符串去重 -XX:StringDeduplicationAgeThreshold=3 达到这个年龄的String对象才会去重

JAVA代码层调优

jvm字节码指令与javap

javap -verbose xxx.class > xxx.txt  生成字节码

基于栈的架构

i++与++i 字符串拼接+原理

for(int i=0;i<10;++i){... 与 for(int i=0;i<10;i++){... 没有区别,因为最终生成的字节码一样

for循环拼接字符串效率低;使用StringBuilder的append效率高

常用代码优化方法
1、尽量重用对象,不要循环创建对象,如,for循环字符串拼接
2、容器类初始化时指定长度  (扩容时有消耗)
3、ArrayList随机遍历快,LinkedList添加删除快
4、集合遍历尽量减少重复计算
5、使用Entry遍历Map
6、大数组复制用System.arraycopy
7、尽量使用基本类型而不是包装类型
8、不要手动调用System.gx()
9、及时消除过期对象的引用,防止内存泄露
10、尽量使用局部变量,减少变量的作用域
11、尽量使用非同步的容器ArrayList VS Vector
12、尽量减少同步作用范围,synchronized方法 VS 代码块
13、ThreadLocal缓存线程不安全的对象,SimpleDateFormat
14、尽量使用延迟加载
15、尽量减少使用反射,加缓存
16、尽量使用连接池、线程池、对象池、缓存
17、及时释放资源,I/O流、Socket、数据库连接
18、慎用异常,不要用抛异常来表示正常的业务逻辑
19、String操作尽量少用正则表达式
20、日志输出注意使用不同的级别
21、日志中参数拼接使用占位符

扩展资料

1、JDK8工具文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/index.html
2、Troubleshooting:https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/
3、jps工具:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jps.html
4、jinfo工具:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jinfo.html
5、jstat工具:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
6、jmap工具:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jmap.html
7、MAT工具:http://www.eclipse.org/mat/downloads.php
8、jstack工具:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstack.html
9、java线程状态:https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr034.html
10、java线程状态转化:https://mp.weixin.qq.com/s/GsxeFM7QWuR--Kbpb7At2w
11、死循环导致CPU负载高:https://blog.csdn.net/goldenfish1919/article/details/8755378
12、正则表达式致死循环:https://blog.csdn.net/goldenfish1919/article/details/49123787
13、jvisualVM:https://visualvm.github.io/index.html
14、apr连接器:http://apr.apache.org/
15、jvm的运行时数据区:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
16、Metaspace:http://ifeve.com/jvm-troubleshooting-guide-4/
17、压缩类空间:https://blog.csdn.net/jijijijwwi111/article/details/51564271
18、CodeCache:https://blog.csdn.net/yandaonan/article/details/50844806
19、GC调优指南:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.html
20、如何选择垃圾收集器:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html
21、G1最佳实践:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#recommendations
22、G1 GC的一些关键技术:https://zhuanlan.zhihu.com/p/22591838
23、CMS日志格式:https://blogs.oracle.com/poonam/understanding-cms-gc-logs
24、G1日志格式:https://blogs.oracle.com/poonam/understanding-g1-gc-logs
25、GC日志分析工具:http://gceasy.io/
26、GCViewer:https://github.com/chewiebug/GCViewer
27、ZGC:http://openjdk.java.net/jeps/333
28、java虚拟机规范:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
29、java语言规范:https://docs.oracle.com/javase/specs/jls/se8/html/index.html
30、javap:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javap.html
31、字段描述符:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2
32、方法描述符:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.3
33、字节码指令:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html
34、常量池:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4
35、本地变量表:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6.1
36、操作数栈:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6.2
37、Code属性:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.3
38、LineNumberTable:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.12
39、constant variable:https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12.4
40、常量表达式:https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28
41、String.intern:https://blog.csdn.net/goldenfish1919/article/details/80410349
42、String去重:https://blog.csdn.net/goldenfish1919/article/details/20233263

 

转载请注明:轻风博客 » Java应用性能监控与调优(二)JVM_GC调优

喜欢 (0)or分享 (0)