一个Java服务(假设PID=10765)出现OOM,如何快速定位?Java服务最常见的OOM原因是:内存分配确实太少,内存确实不够用;如果不释放,内存不断泄漏,导致内存耗尽;某个资源被频繁请求,导致系统资源耗尽,例如:不断创建线程,不断发起网络连接;更具体地说,您可以使用以下工具按照以下步骤进行故障排除。1.确认内存本身是否分配过小方法:jmap-heap10765如上图,可以查看新生代和老年代堆内存的分配大小和使用情况,看是否是自己分配的太小。2、查找最耗内存的对象方法:jmap-histo:live10765|more如上图所示,输入命令后,会以表格的形式显示存活对象的信息,并按照占用内存大小排序:实例数;占用内存大小;班级名称;直观吗?对于实例数多、内存大的实例/类,需要有针对性地审查相关代码。画外音:需要注意的是jmap-histo:live会执行一次FGC。如果还是定位不到,可以转储内存,使用Java内存分析工具MAT(MemoryAnalyzerTool)进行离线分析。上图中占用内存最多的对象是RingBufferLogEvent,一共占用了18M内存,属于正常使用范围。如果发现某个类型的对象占用内存较大(比如好几个G),很可能是该类型的对象创建过多而没有释放。例如:申请资源后,没有调用close()或dispose()释放资源;消费者消费缓慢(或停止消费),而生产者不断向队列中发布任务,导致队列中任务过多;三、确认是否是资源耗尽工具:pstreenetstat查看进程创建的线程数和网络连接数。如果资源耗尽,也可能会出现OOM。这里介绍另一种方法,通过/proc/${PID}/fd/proc/${PID}/task,可以分别查看句柄详情和线程数。比如线上服务器sshd进程的PID为9339,执行:如上图,sshd占用四个句柄:0->标准输入;1->标准输出;2->标准错误输出;3->socket(很容易被认为是监听端口);sshd只有一个PID为9339的主线程,没有多线程。所以只要ll/proc/${PID}/fd|wc-lll/proc/${PID}/task|wc-l(效果等同于pstree-p|wc-l)就可以知道进程和线程打开的句柄数。【本文为专栏作者《58神剑》原创稿件,转载请联系原作者】点此阅读更多该作者好文
