Java程序在运行中常常遇到CPU或内存使用率高的问题,那么应该如何排查问题的起因呢,本文大略形容一下剖析排查办法。
形式一、命令形式剖析
1.排查占用CPU的过程
应用top命令,查找CPU和内存使用率最高的过程获取过程PID。(在大写关上的状况下按P键或者在大写没有关上的状况下按 shift+P键,会依照CPU使用率的高下进行排序,在大写关上的状况下按M键或者在大写没有关上的状况下按 shift+M键,会依照内存使用率的高下进行排序)
2.查找理论占用最高的线程
应用命令top -H -p PID,此处PID就是上一步获取的过程PID,通过此命令能够查看理论占用CPU最高的的线程的ID,此处会有几个TID。
3.获取对应线程的线程栈信息
应用命令printf "%x\n" tid,将线程ID转换为16进制
应用命令jstack PID(过程pid) |grep tid(线程pid16进制) -A 50,此处tid为上一步转换后的16进制,应用此命令能够查看到对应线程的线程栈信息,从对依据线程栈对对应的代码进行剖析。
形式二、dump文件剖析
1.dump文件输入
jmap -dump:live,format=b,file=20170307.dump 9729
file前面的是自定义的文件名,最初的数字是过程的pid
2.应用jvisualvm来剖析dump文件:
jvisualvm是JDK自带的Java性能剖析工具,在JDK的bin目录下,文件名就叫jvisualvm.exe。
jvisualvm能够监控本地、近程的java过程,实时查看过程的cpu、堆、线程等参数,对java过程生成dump文件,并对dump文件进行剖析。
应用形式:间接双击关上jvisualvm.exe,点击文件->装入,在文件类型那一栏抉择堆,抉择要剖析的dump文件,关上。
装入之后在界面右侧的概要、类等选项卡能够看到生成dump文件过后的堆信息
能够看到,dump文件里记录的堆中的实例,总大小大略300M左右,(用第一行的实例大小除以百分比就能算进去),和JVM默认的新生代的大小差不多,远小于JVM中设置的最大堆内存,也就是说,dump文件里记录的并不是实例大小达到最大堆内存时的状态。
为了验证一下,我本人在本地模仿了一下堆内存溢出的情景,并用jvisualvm监控.
剖析:红框框出的局部是产生堆内存溢出时的情景,已应用的堆大小(蓝色局部)并没有增长特地显著,然而申请的堆的大小(黄色局部)从默认的400多兆急速上涨,涨到800M,而后内存溢出,然而应用的堆大小仍然没怎么增长。
所以,dump文件中的实例列表其实是反映了应用的堆的状况,而应用的堆内存并没有达到事后设置的最大堆内存,只是在申请堆内存的过程中超出了事后设置的最大堆内存,而后内存溢出。
通过剖析Dump文件就能够发现程序哪里有死锁。
OOM(Out Of Memory)
为什么会呈现 OOM,个别由这些问题引起
调配过少:JVM 初始化内存小,业务应用了大量内存;或者不同 JVM 区域分配内存不合理
代码破绽:某一个对象被频繁申请,不必了之后却没有被开释,导致内存耗尽
内存透露:申请应用完的内存没有开释,导致虚拟机不能再次应用该内存,此时这段内存就泄露了。因为申请者不必了,而又不能被虚拟机调配给他人用
内存溢出:申请的内存超出了 JVM 能提供的内存大小,此时称之为溢出
内存透露继续存在,最初肯定会溢出,两者是因果关系
比拟常见的 OOM 类型有以下几种:
java.lang.OutOfMemoryError: PermGen space
Java7 永恒代(办法区)溢出,它用于存储已被虚拟机加载的类信息、常量、动态变量、即时编译器编译后的代码等数据。每当一个类首次加载的时候,元数据都会寄存到永恒代
个别呈现于大量 Class 对象或者 JSP 页面,或者采纳 CgLib 动静代理技术导致。
Java8 将永恒代变更为元空间,报错:java.lang.OutOfMemoryError: Metadata space,元空间内存不足默认进行动静扩大。
咱们能够通过 -XX:PermSize 和 -XX:MaxPermSize 批改办法区大小
java.lang.StackOverflowError
栈溢出,个别是因为程序中存在 死循环或者深度递归调用 造成的。如果栈大小设置过小也会呈现溢出,能够通过 -Xss 设置栈的大小
虚拟机抛出栈溢出谬误,能够在日志中定位到谬误的类、办法。
java.lang.OutOfMemoryError: Java heap space
堆内存溢出,溢出的起因个别因为 JVM 堆内存设置不合理或者内存透露导致
如果是内存透露,能够通过工具查看透露对象到 GC Roots 的援用链。把握了透露对象的类型信息以及 GC Roots 援用链信息,就能够精准地定位出透露代码的地位。
如果不存在内存透露,就是内存中的对象的确都还必须存活着,那就应该查看虚拟机的堆参数(-Xmx 与 -Xms),查看是否能够将虚拟机的内存调大些。
线上如遇到 JVM 内存溢出,能够分以下几步排查
1、jmap -heap PID 查看是否内存调配过小。
2、jmap -histo:live PID | more 查看是否有显著的对象调配过多且没有开释状况。
3、jmap -dump:file=./jvmdump.hprof PID导出 JVM 以后内存快照,应用 JDK 自带或 MAT 等工具剖析快照。
如果下面还不能定位问题,那么须要排查利用是否在一直创立资源,比方网络连接或者线程,都可能会导致系统资源耗尽。