一、使用dmesg命令查看系统日志java
dmesg |grep -E ‘kill|oom|out of memory’,能够查看操做系统启动后的系统日志,这里就是查看跟内存溢出相关联的系统日志。mysql
二、这时候,须要启动项目,使用ps命令查看进程面试
ps -aux|grep java命令查看一下你的java进程,就能够找到你的java进程的进程id。sql
三、接着使用top命令缓存
top命令显示的结果列表中,会看到%MEM这一列,这里能够看到你的进程可能对内存的使用率特别高。以查看正在运行的进程和系统负载信息,包括cpu负载、内存使用、各个进程所占系统资源等。框架
四、使用jstat命令jvm
用jstat -gcutil 20886 1000 10命令,就是用jstat工具,对指定java进程(20886就是进程id,经过ps -aux | grep java命令就能找到),按照指定间隔,看一下统计信息,这里会每隔一段时间显示一下,包括新生代的两个S0、s1区、Eden区,以及老年代的内存使用率,还有young gc以及full gc的次数。工具
使用 jstat -gcutil 8968 500 5 表示每500毫秒打印一次Java堆情况(各个区的容量、使用容量、gc时间等信息),打印5次性能
例如:优化
看到的东西相似下面那样:
S0 S1 E O YGC FGC
26.80 0.00 10.50 89.90 86 954
其实若是你们了解原理,应该知道,通常来讲大量的对象涌入内存,结果始终不能回收,会出现的状况就是,快速撑满年轻代,而后young gc几回,根本回收不了什么对象,致使survivor区根本放不下,而后大量对象涌入老年代。老年代很快也满了,而后就频繁full gc,可是也回收不掉。
而后对象持续增长不就oom了,内存放不下了,爆了呗。
因此jstat先看一下基本状况,立刻就能看出来,其实就是大量对象无法回收,一直在内存里占据着,而后就差很少内存快爆了。
五、使用jmap命令查看
执行jmap -histo pid能够打印出当前堆中全部每一个类的实例数量和内存占用,以下,class name是每一个类的类名([B是byte类型,[C是char类型,[I是int类型),bytes是这个类的全部示例占用内存大小,instances是这个类的实例数量。
六、把当前堆内存的快照转储到dumpfile_jmap.hprof文件中,而后能够对内存快照进行分析
使用jmap -dump:format=b,file=文件名 [pid],就能够把指定java进程的堆内存快照搞到一个指定的文件里去,可是jmap -dump:format其实通常会比较慢一些,也能够用gcore工具来导出内存快照
例如:jmap -dump:format=b,file=D:/log/jvm/dumpfile_jmap.hprof 20886
接着就是能够用MAT工具,或者是Eclipse MAT的内存分析插件,来对hprof文件进行分析,看看究竟是哪一个王八蛋对象太多了,致使内存溢出了
或者使用jdk的目录下的bin目录下的:jvisualvm.exe
八、总结:
通常常见的OOM,要么是短期内涌入大量的对象,致使你的系统根本支持不住,此时你能够考虑优化代码,或者是加机器;要么是长时间来看,你的不少对象不用了可是还被引用,就是内存泄露了,你也是优化代码就行了;这就会致使大量的对象不断进入老年代,而后频繁full gc以后始终无法回收,就撑爆了
要么是加载的类过多,致使class在永久代理保存的过多,始终没法释放,就会撑爆
我这里能够给你们最后提一点,人家确定会问你有没有处理过线上的问题,你就说有,最简单的,你说有个小伙子用了本地缓存,就放map里,结果没控制map大小,能够无限扩容,最终致使内存爆了,后来解决方案就是用了一个ehcache框架,自动LRU清理掉旧数据,控制内存占用就行了。
另外,务必提到,线上jvm必须配置-XX:+HeapDumpOnOutOfMemoryError,-XX:HeapDumpPath=/path/heap/dump。由于这样就是说OOM的时候自动导出一分内存快照,你就能够分析发生OOM时的内存快照了,究竟是哪里出现的问题。
九、修改代码调优,修改jvm配置调优,部署接口压测
代码进行优化、根据压测的状况去进行必定的jvm参数的调优,一个系统的QPS,一个是系统的接口的性能,压测到必定程度的时候 ,机器的cpu、内存、io、磁盘的一些负载状况,jvm的表现
十、流程
附加:系统频繁full gc:
比OOM稍微好点的是频繁full gc,若是OOM就是系统自动就挂了,很惨,你绝对是超级大case,可是频繁full gc会好多,其实就是表现为常常请求系统的时候,很卡,一个请求卡半天没响应,就是会以为系统性能不好。
首先,你必须先加上一些jvm的参数,让线上系统按期打出来gc的日志:
-XX:+PrintGCTimeStamps
-XX:+PrintGCDeatils
-Xloggc:<filename>
这样若是发现线上系统常常卡顿,能够当即去查看gc日志,大概长成这样:
若是要是发现每次Full GC事后,ParOldGen就是老年代总是下不去,那就是大量的内存一直占据着老年代,啥事儿不干,回收不掉,因此频繁的full gc,每次full gc确定会致使必定的stop the world卡顿,这是不可能彻底避免的
接着采用跟以前同样的方法,就是dump出来一分内存快照,而后用Eclipse MAT插件分析一下好了,看看哪一个对象量太大了
接着其实就是跟具体的业务场景相关了,要看具体是怎么回事,常见的其实要么是内存泄露,要么就是类加载过多致使永久代快满了,此时通常就是针对代码逻辑来优化一下。
给你们仍是举个例子吧,咱们线上系统的一个真实例子,你们能够用这个例子在面试里来讲,好比说当时咱们有个系统,在后台运行,每次都会一会儿从mysql里加载几十万行数据进来各类处理,相似于定时批量处理,这个时候,若是对几十万数据的处理比较慢,就会致使好比几分钟里面,大量数据囤积在老年代,而后无法回收,就会频繁full gc。
当时咱们其实就是根据这个发现了当时两台机器已经不够了,由于咱们当时线上用了两台4核8G的虚拟机在跑,明显不够了,就要加机器了,因此增长了机器,每台机器处理更少的数据量,那不就ok了,立刻就缓解了频繁full gc的问题了。