本文转载自微信公众号“小姐姐的味道”,可以通过以下二维码关注。转载本文请联系味觉小姐公众号。对于内存问题的排查,搞理论是痛苦的,搞实践也是痛苦的。没有干净的地方。为什么?因为内存溢出是Java码农永远的伤。溢出有很多种解释,有全溢出,有缓冲区溢出攻击,还有一种叫做leadership的溢出。不知道溢出理论是什么,xjjdog在这里科普一下。《领导看了会炸毛的溢出理论》最重要的内存溢出是什么?其实,内存溢出就像一场交通事故。造成事故的一方为特定服务;处理事故的一方是相关的程序员。其中最重要的一个环节就是需要在事故现场拍照取证。如果没有照片,没有行车记录仪,没有证据,就只能靠那张嘴,不管怎么说都不可信。**这句话很重要:检查内存问题最重要的是什么?当然是信息收集,留下一些证据来支持我们排查问题。**不要忽视基础知识,并对解决内存问题感兴趣。那是自虐。有很多工具可以帮助我们定位问题,但前提是你离开它。下面这篇文章是xjjdog很久以前留下的。由于标题,大家可能忽略了,但是这些工具可以帮助我们快速定位问题。ss-antp>$DUMP_DIR/ss.dump2>&1netstat-s>$DUMP_DIR/netstat-s.dump2>&1top-Hp$PID-b-n1-c>$DUMP_DIR/top-$PID.dump2>&1sar-nDEV12>$DUMP_DIR/sar-traffic.dump2>&1lsof-p$PID>$DUMP_DIR/lsof-$PID.dumpiostat-x>$DUMP_DIR/iostat.dump2>&1free-h>$DUMP_DIR/free.dump2>&1jstat-gcutil$PID>$DUMP_DIR/jstat-gcutil.dump2>&1jstack$PID>$DUMP_DIR/jstack.dump2>&1jmap-histo$PID>$DUMP_DIR/jmap-histo.dump2>&1jmap-dump:format=b,file=$DUMP_DIR/堆.bin$PID>/dev/null2>&1GClogconfiguration但不是每次出问题都是你在机器这边。手动无法保证实时性能。所以,强烈建议大家输出更详细的GC日志,这样出现问题的时候心情会更舒畅。实际上,这个要求在我看来是强制性的。很多同学上来都说我内存溢出来了。但是你和它需要一些日志信息、堆栈和现场保存的快照。没有什么。这纯粹是为了好玩。下面是JDK8以下的GC日志参数,可以看出还是很长的。#!/bin/shLOG_DIR="/tmp/logs"JAVA_OPT_LOG="-verbose:gc"JAVA_OPT_LOG="${JAVA_OPT_LOG}-XX:+PrintGCDetails"JAVA_OPT_LOG="${JAVA_OPT_LOG}-XX:+PrintGCDateStamps"JAVA_OPT_LOG="${JAVA_OPT_LOG}-XX:+PrintGCApplicationStoppedTime"JAVA_OPT_LOG="${JAVA_OPT_LOG}-XX:+PrintTenuringDistribution"JAVA_OPT_LOG="${JAVA_OPT_LOG}-Xloggc:${LOG_DIR}/gc_%p.log"JAVA_OPT_OOM="-X+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=${LOG_DIR}-XX:ErrorFile=${LOG_DIR}/hs_error_pid%p.log"JAVA_OPT="${JAVA_OPT_LOG}${JAVA_OPT_OOM}"JAVA_OPT="${JAVA_OPT}-XX:-OmitStackTraceInFastThrow"下面是JDK9及以上版本的日志配置。可以看到它的配置方式完全变了,不向下兼容了。Java做的这个改动还是挺蛋疼的。#!/bin/shLOG_DIR="/tmp/logs"JAVA_OPT_LOG="-verbose:gc"JAVA_OPT_LOG="${JAVA_OPT_LOG}-Xlog:gc,gc+ref=debug,gc+heap=debug,gc+age=trace:file=${LOG_DIR}/gc_%p.log:tags,uptime,time,level"JAVA_OPT_LOG="${JAVA_OPT_LOG}-Xlog:safepoint:file=${LOG_DIR}/safepoint_%p.log:tags,uptime,时间,级别"JAVA_OPT_OOM="-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=${LOG_DIR}-XX:ErrorFile=${LOG_DIR}/hs_error_pid%p.log"JAVA_OPT="${JAVA_OPT_LOG}${JAVA_OPT_OOM}"JAVA_OPT="${JAVA_OPT}-XX:-OmitStackTraceInFastThrow"echo$JAVA_OPT一旦发现问题,可以通过GC日志快速定位到堆中的问题。但是不是让你一行一行的看,那样效率太低了。因为日志可能会很长,而且可能看不懂。这时候可以借助一些在线工具来辅助解决。我经常使用gceasy,这里是它的截图。http://gceasy.io有GC日志是不够的,因为它只记录堆空间的一些变化。至于操作系统的一些资源变化,它无从知晓。所以,如果你有一个监控系统,它也可以在查找问题时提供帮助。从下图中可以看出系统资源的一些变化。溢出示例堆溢出代码。日志。java-Xmx20m-Xmn4m-XX:+HeapDumpOnOutOfMemoryError-OOMTest[18.386s][info][gc]GC(10)ConcurrentMark5.435ms[18.395s][info][gc]GC(12)PauseFull(AllocationFailure)18M->18M(19M)10.572ms[18.400s][info][gc]GC(13)PauseFull(AllocationFailure)18M->18M(19M)5.348msExceptioninthread"main"java.lang.OutOfMemoryError:JavaheapspaceatOldOOM.main(OldOOM.java:20)jvisualvm响应。元空间溢出代码。日志。java-Xmx20m-Xmn4m-XX:+HeapDumpOnOutOfMemoryError-XX:MetaspaceSize=16M-XX:MaxMetaspaceSize=16MMetaspaceOOMTest6.556s][info][gc]GC(30)ConcurrentCycle46.668msjava.lang.OutOfMemoryErrors6/javatumpheid3/javatumping.3.hprofjvisualvm反应。直接内存溢出代码。日志。java-XX:MaxDirectMemorySize=10M-Xmx10MOffHeapOOMTestException在线程"Thread-2"java.lang.OutOfMemoryError:Directbuffermemoryatjava.nio.Bits.reserveMemory(Bits.java:694)atjava.nio.DirectByteBuffer.
