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 等工具剖析快照。
如果下面还不能定位问题,那么须要排查利用是否在一直创立资源,比方网络连接或者线程,都可能会导致系统资源耗尽。