JVM内存溢出定位和调优

JVM规划

java采用自动内存管理,我们可以更加关注业务的逻辑,JVM对语言来说是透明的,只要符合JVM规范就可以跑在JVM上。就像《深入理解Java虚拟机》里说的,如果热爱技术,想一直走下去那么JVM是必经之路,我亦是苦行人。对于系统调优来说,我觉得离不开实际的测试,用多大内存,CPU,具体怎么设置满足当前需求、并发等,怎样编码更安全性能更好,还是挺有挑战的吧。

定位解决现网问题

车祸现场:频繁FullGC、内存溢出,这是比较严重的问题了。当然问题总是五花八门的,比如运维参数配置错误等等…
现在的单位开发接触不到现网的,都是运维给你发邮件,直接拿dump,日志等进行分析。
FullGC造成的CPU100%怎么办(只针对java进程):

  1. top:查看CPU比较高的进程。
  2. 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的参数会越来越少,毕竟人是不靠谱的。
    根据具体业务选择合适垃圾回收器!
  1. 响应时间、停顿时间 [CMS G1 ZGC] (需要给用户作响应)
  2. 吞吐量 = [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的堆空间占用比例
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值