内存爆炸、CPU100%问题定位"/>
内存爆炸、CPU100%问题定位
目录
- 一、内存爆炸相关
- 1、关于-Xms(最小堆内存)和-Xmx(最大堆内存)
- 2、JVM初始化时申请实际物理内存
- 3、OutOfMemory问题排查
- (1) 堆内存溢出排查
- (2) 堆外内存溢出排查
- 二、CPU 100及死锁问题定位
- 1、CPU 100问题排查
- (1) 找到程序对应进程号
- (2) 查找进程对应的线程编号
- (3)查看线程堆栈信息
- 2、死锁问题排查
一、内存爆炸相关
1、关于-Xms(最小堆内存)和-Xmx(最大堆内存)
最大堆内存为JVM能向操作系统申请的最大内存。最小堆内存并不是程序一启动就申请这么多内存,而是当前进程如果申请的内存已超过最小堆内存,内存回收时,大于最小堆内存的部分会返回给操作系统,其它申请的内存不会。
2、JVM初始化时申请实际物理内存
默认没有配置-XX:+AlwaysPreTouch
参数时,JVM进程申请的内存只是虚拟内存,程序运行时根据虚拟内存地址映射到实际的物理内存,缺页时才将数据加载到物理内存,分配实际的物理空间。加上-XX:+AlwaysPreTouch
参数后,程序将直接分配到实际的物理内存,而不是根据需要在缺页时才申请物理内存。
简单来说就是没有开启预热时,JVM只是向操作系统做了登记,告诉操作系统我需要多少内存,但实际上并没有进行物理分配,加上该参数后在程序启动时就会分配物理内存。
注意:加上该参数可能会导致程序启动变慢。
3、OutOfMemory问题排查
(1) 堆内存溢出排查
内存溢出示例如下:
/*** -Xmx512m -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\Administrator\Desktop\java_heapdump.hprof*/
public class OutOfMemoryExample1 {private static List<Object> space = new ArrayList<>();public static void main(String[] args) throws Exception {// 内存泄漏 最终会导致 = 内存溢出for (int i = 0; i < 1000; i++) {space.add(new byte[1024 * 1024 * 128]);Thread.sleep(3000L);}}
}
通过MAT(Eclipse Memory Analyzer)工具查找leak suspect
即可排查:
(2) 堆外内存溢出排查
直接内存溢出示例:
/*** 堆外内存溢出* 控制堆外内存大小:-XX:MaxDirectMemorySize=128m -XX:+HeapDumpOnOutOfMemoryError*/
public class OutOfMemoryExample2 {private static List<Object> space = new ArrayList<>();public static void main(String[] args) throws Exception {// 内存泄漏 最终会导致 内存溢出for (int i = 0; i < 1000; i++) {ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024 * 1024 * 64);byteBuffer.put(new byte[1024 * 1024 * 5]);space.add(byteBuffer);Thread.sleep(2000L);}}
}
对于堆外内存溢出的情况,通过jvisualvm中的btrace插件分析调用情况,如下:
二、CPU 100及死锁问题定位
1、CPU 100问题排查
CPU占用率过高通常是因为死循环或者递归调用导致的,通过jstack基本能定位到具体是哪些线程在执行CPU高度密集型操作。示例代码如下:
public class Cpu100Example {public static void main(String[] args) {System.out.println("Starting executing the programme");// 如果有多核,开启与逻辑cpu相等的线程数执行计算型任务即可,不要有阻塞或者I/O操作while (true) {new Random().nextInt();}}
}
问题解决思路如下:
(1) 找到程序对应进程号
通过jps或者jcmd命令可查到程序进程号,如下:
[universe@VM_0_13_centos ~]$ jps -l
2792 org.apache.zookeeper.server.quorum.QuorumPeerMain
2745 org.apache.zookeeper.server.quorum.QuorumPeerMain
30858 comease.issue.cpu.Cpu100Example
2716 org.apache.zookeeper.server.quorum.QuorumPeerMain
31822 sun.tools.jps.Jps
(2) 查找进程对应的线程编号
top -H -p 30858
结果如下:
从上图可以看到,pid为30859
的线程几乎占用了全部CPU。
(3)查看线程堆栈信息
jstack
命令内容中的nid为上一步我们获取到的线程pid的16进制形式,因此先把线程pid转换为16进制,再对jstack
内容进行查找。
现在可以看到该线程的方法调用栈,进而找到相应的代码。
2、死锁问题排查
示例代码如下:
public class DeadLockExample {private static Object monitor1 = new Object();private static Object monitor2 = new Object();public static void main(String[] args) {new Thread(new ObtainLockTask1()).start();new Thread(new ObtainLockTask2()).start();}private static class ObtainLockTask1 implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " starts to obtain lock from monitor1");try {while (true) {synchronized (monitor1) {System.out.println(Thread.currentThread().getName() + " has obtained lock from monitor1");Thread.sleep(3000);synchronized (monitor2) {System.out.println(Thread.currentThread().getName() + " starts to obtain lock from monitor2");}}}} catch (InterruptedException e) {e.printStackTrace();}}}private static class ObtainLockTask2 implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " starts to obtain lock from monitor2");try {while (true) {synchronized (monitor2) {System.out.println(Thread.currentThread().getName() + " has obtained lock from monitor2");Thread.sleep(3000);synchronized (monitor1) {System.out.println(Thread.currentThread().getName() + " starts to obtain lock from monitor1");}}}} catch (InterruptedException e) {e.printStackTrace();}}}
}
依然通过jstack命令查看线程堆栈信息可以定位,如下图:
注意:通过
synchronized
关键字导致的Java平台级死锁通过jstack
命令可以直接分析出来,而使用ReentrantLock
导致的死锁则不能。
更多推荐
内存爆炸、CPU100%问题定位
发布评论