你们线上突发OOM,是如何快速定位OOM问题?

当我们的请求进来,需要创建对象,那就需要去内存中申请空间,这时候如果内存满了就会触发FullGC,GC之后如果内存依然爆满,这时候就会出现

OutOfMemeryError的异常。

那如果说OOM我们的应用会挂掉吗?其实不一定,如果某些请求不需要申请堆内存空间,依然可以正常请求

如果说并发量非常高,并且需要大量申请内存,这时候我们的应用进程就会飙高,进而操作系统意识到该进程比较危险,会把进程给杀掉。

从而我们的应用就会挂掉。所以OOM异常并不一定就会导致我们的应用挂掉,但是会有直接挂掉的风险,所以这是一个非常严重的故障

线上出现OOM如何定位和解决呢?

导致OOM异常的原因可能会有很多:

 1.一次性申请对象太多。

更改申请对象数量

比如数据列表查询,一次性去数据库查询所有数据,如果数据量达到了千万级,所有数据都放到了List当中,那有可能就会造成内存溢出。

解决这个问题:

只需要去更改申请对象的数量,比如分页,通过条件限制数量等。

2.内存资源耗尽未释放(内存泄漏)

找到未释放的对象进行释放

比如使用线程或者数据库查询,在高并发下,假如我们不断去创建线程,不断创建jdbcConnection,但是又没有去释放,那久而久之就会造成内存溢出,

这种情况要解决我们可以及时去释放。比如connection用完就关闭,当然可以引用池化的思想,也就是最多只申请100个资源或者10个资源,用到了这个

我就阻塞不再申请了,这种方式就可以解决这种内存资源耗尽未释放的问题。

3.本身资源不够

jmap -heap查看堆信息

本身应用分配堆内存资源就不够,无法支撑应用日常的操作。比如应用当中本身就有一些比较大的对象,要支撑应用日常的基本业务操作,堆内存如果

不够的话,肯定就需要调整堆内存。

===

jmap -heap 24088,打印当前这个java应用他所使用堆的最大内存(MaxHeapSize),新生代使用了多少内存(New Generation,capacity,used,free,Eden Space,

s0,s1)(老年代(tenured generation,capacity,used,free))。根据这些指标,再结合应用,可以适当做一些调整,这里你必须要对jvm的一些概念有所了解,

堆内存不够的话,可以通过这个命令进行查看,然后进行相应调整。

针对第1和第2个问题,我们应该怎么解决呢?我们应该定位到对应的业务代码,比如一次性申请对象太多,我肯定要找到对应的业务代码,然后改一下他申请对象的数量。

如果我们的某一个资源没有释放,我肯定得找到对应的代码,然后及时释放掉。所以这个问题我们得去定位,也就是怎么快速的去定位线上的OOM。

我们要定位OOM的话,我们也分不同的情况。

1.系统已经OOM挂了

提前设置-XX:_HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=

2.系统运行中还未OOM

导出dump文件:jmap -dump:format=b,file=xxx.hprof 14660

Arthas

3.结合jvisualvm进行调试

查看最多跟业务有关对象--找到GCRoot--查看线程栈

======

第一种就是如果你的系统挂掉了的话,怎么定位?

第二种就是如果你的系统正在运行,还没有OOM,怎么定位?

我们先看第一种,如果你的系统如果挂掉了,该如何定位呢?我们通常会通过一个叫做堆的dump文件,这样我们才可以有效的快速定位。如果你的系统已经挂掉了,并且没有在

运行你的程序的时候去设置(-XX:_HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=)这个jvm参数的话,那他就不会在你应用OOM的时候为你导出一个堆的dump文件,

导出到你指定的目录当中,通过这个dump文件,我们就可以来分析oom溢出的具体代码位置。

所以如果你没有设置这个参数的话,那你可以提桶跑路了,因为你无据可查。

你没有去给他设置这个参数的话,那你可能就无据可查了,当然你有可能能够根据这个线程栈中的异常信息能够追溯到异常的位置,

但是我们真正线上的这个系统往往要复杂的多,因为我们会有很多的线程,所以根据异常信息是很难定位的。

所以建议大家可以将你的系统无脑地去设置(-XX:_HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=)这个参数。

但是有一点,你一定要保证你的系统的硬盘空间足够大,因为它会记录系统在整个运行过程当中所有的一些对象的信息,所以也是非常占磁盘空间的。也就是它导出的时候,有可能这个文件会很大,但是能够在你出现oom时候能够快速定位。

示例:

java -Xms10M -Xmx10M -XX:_HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./jvm_logs/ -jar jvm-demo.jar

内存溢出:

java.lang.OutOfMemoryError:Java heap space

Dumping heap to ./jvm_logs/java_pid24232.hprof

导出文件到windows硬盘,用jvisualvm工具导入dump文件,文件类型选择堆Dump,否则会选不到。

我们最主要看类,可以看到在应用运行当中所用到的实例,实例数,占用内存大小,我们肯定要找到最占内存的一个实例,像char,String,Integer我们可能无法明确定位到,

但是看到里面的User,就是我们应用程序中用到的,我们可以找到在业务当中用到的一些对象,找到GCRoot(三角形标识)通过GCRoot显示当前GCRoot它的线程引用,

ArrayList--UseService.getUsers(UserService.java:17)(这个方法的第17行),分析代码造成OOM的原因。

通过dump文件+jvisualvm就可以很快的定位到OOM问题。

这是一种情况,就是我们系统已经挂掉了,建议大家在运行的时候加上(-XX:_HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./jvm_logs/)这个参数,那么你就可以通过dump文件

来快速的定位。

第二种情况,我们的系统还没有挂掉,要分析,该怎么办?

当然你可以在它运行的阶段去导出一个dump文件,或者利用Arthas这种调试工具来进行调试。

在系统运行阶段去导出dump文件,会造成一次full gc,会造成stw(stop the world),也就是所有的线程都会中断,但是你要知道,如果你不导出这个dump文件的话,你要付出成倍的时间

去定位。

示例:

java -jar jvm-demo.jar(java -jar jvm-demo.jar &则是后台运行)

如果说我们不导出dump文件的话,我们只能通过命令jps找到java进程,jmap -histo:live 24286。

我们可以看到,它会把最占内存的这些对象显示出来,其实就是我们刚刚利用jvisualvm的面板,其实通过工具操作出来的可视化界面也是使用这些命令。

如果用命令方式,只能说看到好像user比较多,就没办法像刚才那样能够快速定位它的gcRoot,线程引用,所以要付出更加成倍的时间去定位问题。

或者说使用像Arthas这种故障的调试工具。

所以为了快速定位OOM,建议大家去使用这个命令(jmap -dump:format=b,file=./jvm_logs/xxx.hprof 24286)。

之后和方式一(方式一是系统挂掉自动导出dump文件。方式二是系统还没挂,主动导出dump文件)一样利用刚才的思路,利用xftp把dump文件拿到windows环境,将dump文件导入到jvisualvm中进行定位。

虽然这种方式会造成gc和stw,但是为了更加快速定位oom,这种方式是值得的。

总结:

1.提前设置-XX:_HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=。

2.导出dump文件。

3.结合jvisualvm导入dump文件,分析定位问题。

 

posted @ 2024-05-23 15:18  super超人  阅读(572)  评论(0编辑  收藏  举报