垃圾收集器
垃圾对象判断标准有:引用计数法和跟搜索法
垃圾对象回收算法有:标记清除、复制、标记整理、分代回收
垃圾收集器
实现表示可以搭配使用,虚线表示当内存分配失败的时候cms会退化成SerialOld。
Serial、SerialOld、ParNew为串行。
Paralle、ParallelOld为并行。
cms并发。
搭配原则:
1、老年代选择cms,新生带只能选择串行
2、老年代选择并行,新生代只能选择并行
3、老年代选择串行,新生代可以选择所有类型
串行收集器
-XX:+SerialGC
Serial+SerialOld:该组合是client模式下默认组合,单线程stw方式,当需要回收的时候,设置停顿标识,等所有用户线程进入安全点后开始进行垃圾回收。
并行收集器
-XX:+UseParallelGC或-XX:+UseParallelOldGC(可互相激活)
Parallel Scavenge + Parallel Old:server模式下默认组合,高吞吐量
cms收集器原理
-XX:+UseConcMarkSweepGC
ParNew + CMS + Serial Old:CMS失败降级为Serial Old。
标记清除的算法,会产生内存碎片,通过-XX:CMSFullGCsBeForeCompaction设置多少次full gc后堆内存进行压缩(默认为0,每次都压缩)
cms工作可以分为四步:
1、初始标记
stw,标记gcroot
2、并发标记
与用户线程共同执行,有两个问题需要解决,一个是当并发标记完成之后用户线程又会产生一些新的垃圾(浮动垃圾),这个没办法解决,只能等到下次gc时处理。另一个问题看下面:
比如对象的引用关系a->b->c
在初始标记阶段找出a标记为根节点
并发标记阶段枚举a
首先将a标记为灰色表示正在枚举a
找到了b,接下来要开始枚举b,那么把a变黑,b变灰
这个时候用户线程把引用关系变成了a->c
cpu又切换到gc,继续枚举b,发现c已经不在b上了
最终完成标记的时候c还是白色的,标识无根在引用可以清除
如何解决这个问题,三色标记法,就是在并发标记阶段,如果改变c的引用的时候,也就是改成了a->c时,要判断一下a是否为黑色,如果a为黑色要把c标记成灰色。
3、重新标记
stw,把并发标记阶段发生变化的部分再次标记一下
4、最终清除
过程中最耗时的是并发标记,但是与用户线程共同存在。
G1
1、并行+并发
2、分代收集
3、可预测停顿
4、将整个堆分为多个region,新生代和老年代都是一部分region的集合。
5、跟踪各个region中垃圾回收价值(回收能回收空间大小以及需要的时间),维护一个优先级列表。
每次进行回收的时候,根据允许的回收时间,选择回收价值最高的region进行回收
6、跨region引用,回收时如何处理?
有两个region A和B,A中有个对象a引用了B中对象b,当堆B进行回收的时候,枚举根节点也只是针对B的根节点,那么b对象怎么半?
每个region维护一个remembered set,当发生引用的更新操作的时候,会记录到这个set中(当a引用b发生的时候,会把这个关系记录到B的remembered set中),当回收B的时候,不只枚举B的根节点,还要枚举B的remembered set。
7、四个步骤:
初始标记:标记出gcRoot,同时修改TAMS的值,该值记录哪些region是可对用户开放,可实现与用户线程并发。
并发标记:
重新标记:并发标记阶段发生变化的部分被记录到rememberred set logs中,该阶段把这些logs合并到remembered set中
筛选回收:对region进行排序,按照回收价值选择性对region进行回收
jvm调优
并行收集器通过下面两个参数让jvm自行分配其他参数
-XX:MaxGCPauseMillis=< N > 垃圾回收最大停顿时间
-XX:GCTimeRatio=< N > 垃圾回收停顿时间占比
当配置了上边两个参数的时候,jvm为了达到上边的效果,会自动设置一些相关其他参数。
推荐使用上面参数
常见参数
正常情况用jvm默认配置就可以,但有一些特殊应用需要自己来配置参数。
-XX:SurvivorRatio
eden与s0/s1的比例,这个值很重要,决定年轻代的利用率,比如默认为8比1,则年轻代占用达到百分90的时候才会发生minor gc
-XX:MaxTenuringThreshold(最大老年代阀值)
年轻代中对象到达多少岁时进入老年代,如果设置为0,则对象不进入年轻代直接在老年代分配,当应用老年代较多时这样可以提高效率。 linux64 jdk6默认值为15.
-XX:NewSize
-XX:MaxNewSize
设置年轻代大小初始值和最大值,这个根据实际应用设置,如果年轻代临时对象多就设置大一些,一般为堆大小的三分之一、四分之一(默认三分之一)。这两个值要设置为一样的。
-XX:+UseCMSCompactAtFullCollection
full gc后对内存进行一次压缩,减少内存碎片
-XX:CMSFullGCsBeforeCompaction
设置多少次full gc后在压缩内存
-XX:CMSInitiatingOccupancyFraction
设置老年代使用百分比达到多少在进行full gc,默认为百分92
-XX:+CMSScavengeBeforeRemark
full gc前先yong gc
年轻代年老代设置具体该设置多大?
这个不是绝对的数字,需要根据自己的应用一点一点的去调优。大的年轻代必然导致小的年老代。大的年轻代导致minor gc次数少,小的年老代导致gull gc频繁。如果应用有大量大对象存在,适合调大年老代。
在应用没有显着某方面特征的时候,可以本着下面的原则分配:
2、观察应用峰值的时候看一下老年代的内存情况,如果剩余很多,可以尝试增大年轻代,前提是不会增加老年代full gc频率。
4、栈大小默认是1m,大多数实际用不了这么多,可以尝试设置为256k,这样可以支持更大的线程数
gc日志
在tomcat中配置打印gc日志
[root@01server ~]# vim /usr/local/tomcat-8.5.32/bin/catalina.sh
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution"
[root@01server ~]# shutdown.sh # 重启Tomcat
...
[root@01server ~]# startup.sh
...
[root@01server ~]# ls /usr/local/tomcat-8.5.32/logs/ |grep gc.log # 有生成gc.log这个文件代表成功
gc.log
[root@01server ~]#
-XX:+PrintGCDetails 打印GC的详细信息
-XX:+PrintGCTimeStamps、-XX:+PrintGCDateStamps 打印GC的时间
-Xloggc:$CATALINA_HOME/logs/gc.log 指定GC日志文件所存放的路径
-XX:+PrintHeapAtGC 在GC发生的前后打印堆信息
-XX:+PrintTenuringDistribution 打印对象的年龄信息
gc日志怎么看呢?
通过上面的日志可以分析出哪些信息?
yong gc:回收前内存占用多少、回收后内存占用多少、回收掉了多少、有多少进入老年代、频率、每次回收耗时
full gc:回收前内存占用多少、回收后内存占用多少、回收掉了多少、频率、每次回收耗时
通过分析以上参数哪些不正常,可以判断出调优方案。
调优
jvm调优的目标是什么
gc时间少、gc次数少、full gc频率低。
前两个目标是相悖的,只能根据实际情况取平衡。
OutOfMemoryException内存泄漏有哪些现象?如何解决?
现象:gc时间越来越长、full gc越来越频繁、每次full gc老年代回收空间很少
如何解决:
1、生成dump文件。
可以通过jvm参数(XX:+HeapDumpOnOutOfMemoryErro)设置当系统发生内存泄漏时生成文件。也可以通过jmap生成。
2、分析dump文件。
通过工具visual vm可以打开dump文件。可以查看内存各个区域情况,类加载情况,trace跟踪,线程情况,基本上所有想要的信息都可以看到。
jdk调优命令
jstat:实时查看内存状况。
jmap:查看对象状况
jstack:查看jvm栈状况
jinfo:查看jvm配置参数信息
内存、cpu占用高,如何排查
查看问题进程,得到进程PID
top -c
查看进程里的线程明细,并手动记下CPU异常的线程PID
top -p PID -H
使用jdk提供jstack命令打印出项目堆栈
jstack pid > xxx.log
线程PID转成16进制,与堆栈中的nid对应,定位问题代码位置
内存:
查看内存中对象分配情况,找出业务相关的类。
jmap -histo:live PID > xxx.log
通过简单的统计还是没法定位问题的话,就输出内存明细来分析。这个命令会将内存里的所有信息都输出,输出的文件大小和内存大小基本一致。而且会导致应用暂时挂起,所以谨慎使用。
jmap -dump:live,format=b,file=xxx.hprof PID
最后对dump出来的文件进行分析。文件大小不是很大的话,使用jdk自带的jhat命令即可:
jhat -J-mx2G -port 7170
dump文件太大的话,可以使用console vm等工具。
GC:
jstat -gc PID
这里简单介绍一下java8里面这个命令得出的列表各个列的含义:
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间