JVM规划
java采用自动内存管理,我们可以更加关注业务的逻辑,JVM对语言来说是透明的,只要符合JVM规范就可以跑在JVM上。就像《深入理解Java虚拟机》里说的,如果热爱技术,想一直走下去那么JVM是必经之路,我亦是苦行人。对于系统调优来说,我觉得离不开实际的测试,用多大内存,CPU,具体怎么设置满足当前需求、并发等,怎样编码更安全性能更好,还是挺有挑战的吧。
定位解决现网问题
车祸现场:频繁FullGC、内存溢出,这是比较严重的问题了。当然问题总是五花八门的,比如运维参数配置错误等等…
现在的单位开发接触不到现网的,都是运维给你发邮件,直接拿dump,日志等进行分析。
FullGC造成的CPU100%怎么办(只针对java进程):
- top:查看CPU比较高的进程。
- top -Hp [pid]:查看进程中的线程。这些线程可能是GC线程也可能是业务线程。
在这里可能会出现3种情况。
- 第一种情况,某个线程CPU利用率一直100%,则说明是这个线程有可能有死循环,那么请记住这个PID。
- 第二种情况,某个线程一直在TOP 10的位置,这说明这个线程可能有性能问题。
- 第三种情况,CPU利用率高的几个线程在不停变化,说明并不是由某一个线程导致CPU 偏高。
如果是第一种情况,可能是代码问题,也有可能是GC造成,可以用jstat命令看一下GC情况,看看是不是因为持久代或年老代满了,产生Full GC,导致CPU利用率持续飙高,命令如下。
jstat -gcutil/-gc [进程pid] 1000 5 (1秒打印一次,共5次,看图)
还可以把线程dump下来,看看究竟是哪个线程、执行什么代码造成的CPU利用率高。执行 以下命令,把每个线程dump到文件dump01.dump里。执行如下命令。
jstack [进程pid] > /testgc/dump01.dump (Tomcat线程如图)
dump出来的线程ID(nid)是十六进制的,而我们用TOP命令看到的线程ID是十进制的,所 以要用printf命令转换一下进制。然后用十六进制的ID去dump里找到对应的线程。
printf “%x\n” 31558
到此为止我们知道了应用目前的运行状况,那么FullGC,OOM如何定位:
方法一: jmap -histo [进程PID] | head -20
方法二: 设置-XX:+HeapDumpOnOutOfMemoryError参数
方法三: jmap -dump:format=b,file=xxx.hprof [进程PID] (hprof-堆转储文件)
根据实际情况进行选择。
其它问题造成CPU100%(只针对java进程):
生成dump文件
jstack [进程pid] > /testgc/dump01.dump
查看所有线程状态:
grep java.lang.Thread.State dump01.dump | awk ‘{print $2$3$4$5}’ | sort | uniq -c
jstack 定位线程状况,重点关注:WAITING BLOCKED
举个栗子:
waiting on <0x000000xxxxxx3310> (a java.lang.Object)
假如有一个进程中100个线程,很多线程都在waiting on ,一定要找到是哪个线程持有这把锁
怎么找?搜索jstack dump的信息,找<0x000000xxxxxx3310> ,看哪个线程持有这把锁RUNNABLE
注意:每个线程要有名字,请遵循阿里巴巴编码规范。避免new Thread(Task)这种东西来同步订单,这对定位问题造成极大障碍!dump文件的每个开头就是线程名!
参数总结
主要记录JVM的一些常用参数和对应解决的问题。
查询参数:
- java -XX:+PrintFlagsInitial -version | grep GC
- java -XX:+PrintFlagsFinal -version | grep GC
以HelloGC.java为例:
图片
常见垃圾回收选型(JDK1.8)
- -XX:+UseSerialGC = Serial New (DefNew) + Serial Old 小型程序(单cpu)
- -XX:+UseParNewGC = ParNew + SerialOld 很少用使用
- -XX:+UseConc[urrent]MarkSweepGC = ParNew + CMS + Serial Old
- -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认)
- -XX:+UseParallelOldGC = Parallel Scavenge + Serial Old
- -XX:+UseG1GC = G1
只针对JDK1.8,有的垃圾回收器已经在更高的版本已经废弃。可以预见未来JVM的参数会越来越少,毕竟人是不靠谱的。
根据具体业务选择合适垃圾回收器!
- 响应时间、停顿时间 [CMS G1 ZGC] (需要给用户作响应)
- 吞吐量 = [PS]
GC日志
java -Xloggc:/opt/portal/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause HelloGC
GC常用参数
- -Xmn -Xms -Xmx -Xss 年轻代 最小堆 最大堆 栈空间
- -XX:+HeapDumpOnOutOfMemoryError 宕机导出dump
- -XX:MaxTenuringThreshold 升代年龄,最大值15 ,适当调高抑制FGC
- -XX:+DisableExplictGC 让System.gc()不管用
- -XX:+PrintGCDetails 打印GC详细日志信息(新生代,老年代,永久代)
- -XX:+PrintGCTimeStamps 打印GC发生的时间戳
- -XX:+PrintGCCause 打印GC堆日志(不详细)
- -XX:+PrintGC 打印到屏幕,和PrintGCCause类似
- -XX:+PrintHeapAtGC 打印每次GC的次数和当前GC的具体堆情况
- -XX:+PrintVMOptions 打印在运行时,打印虚拟机接收到命令行显示参数(我们给配置的参数)。
- -XX:+PrintFlagsFinal
- -XX:+PrintFlagsInitial
Parallel常用参数
中规中矩,侧重吞吐量,CMS侧重响应速度。参考《JVM》p94
- 吞吐量相关:
-XX:MaxGCPauseMillis 最大垃圾收集停顿时间 (响应)
-XX:GCTimeRatio 0-100,gc收集时间占全部时间比例,默认99;1%是垃圾收集时间(吞吐)
-XX:SurvivorRatio=8 Eden80%和survivor20%(s1=s2)的比例
-XX:PretenureSizeThreshold 大对象到底多大
-XX:+ParallelGCThreads 并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
-XX:+UseAdaptiveSizePolicy 自动选择各区大小比例和其它参数细节(自适应调节策略)
CMS常用参数
-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会尝试用各种手段达到这个时间,比如减小年轻代
G1常用参数
- 划时代的垃圾收集器,面向堆的任何位置Collection Set,不在是Minor、Mijor、Full GC。Region内存的每块分区,取值1-32Mb,可以动态扮演Eden,Survivor,超过G1HeapRegionSize 的设定被判定为Humongous Region,老年代的概念。
-XX:+UseG1GC
-XX:MaxGCPauseMillis 用户期望GC收集停顿时间(STW),G1会优先处理回收价值最大的Region。值默认200
-XX:GCPauseIntervalMillis
-XX:+G1HeapRegionSize 分区大小,建议逐渐增大该值,1 2 4 8 16 32。 随着size增加,垃圾的存活时间更长,GC间隔更长,但每次GC的时间也会更长 ZGC做了改进(动态区块大小)
G1NewSizePercent 新生代最小比例,默认为5%
G1MaxNewSizePercent 新生代最大比例,默认为60%
GCTimeRatio GC时间建议比例,G1会根据这个值调整堆空间
ConcGCThreads 线程数量
InitiatingHeapOccupancyPercent 启动G1的堆空间占用比例