性能测试中常见问题的排查方法

内存溢出和内存泄露

内存溢出 out of memory

内存泄露 memory leak

样例测试代码

问题的排查

CPU使用率过高

样例测试代码

负载过高使用率低

内存溢出和内存泄露

内存溢出 out of memory

是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory

内存泄露 memory leak

是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。memory leak会最终会导致out of memory!较为常见的有java.lang.OutOfMemoryError: Java heap space

样例测试代码

public class TestHeapOOM {
    static class Key {
        Integer id;

        Key(Integer id) {
            this.id = id;
        }

        @Override
        public int hashCode() {
            return id.hashCode();
        }
    }

    public static void main(String[] args) {
        Map m = new HashMap<>();
        while (true) {
            for (int i = 0; i < 10000; i++) {
                Key key = new Key(i);
                if (!m.containsKey(key)) {
                    m.put(key, "Number:" + i);
                }
            }
        }

    }
}

打成jar包后,运行

java -Xms512m -Xmx512m -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:./gc.log -jar TestHeapOOM-1.0-SNAPSHOT.jar &

运行一段时间后抛出:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.lang.Integer.valueOf(Integer.java:832)
        at com.wosai.qa.test_heap_oom.main(test_heap_oom.java:27)

堆内存一直增加,没有下降趋势(回收速度赶不上增长速度), 最后不断触发FullGC, 甚至crash

通过visualgc可以看到老生代持续增加

通过jstat可以看到老生代持续增加

分析gc日志可以看到多从老生代gc无法回收内存后,触发Full GC,之后多次Full GC后进程crash

问题的排查

  • jmap -histo:live pid查看jvm堆中具体情况

注意:jmap -histo:live 会触发Full gc,请谨慎使用

  • 导出整个JVM 中内存信息,通过其他工具导入文件再分析,比如jvisualvm jmap -dump:format=b,file=文件名 [pid]

CPU使用率过高

样例测试代码

public class TestCpuHighUsage {
    public static void main(String[] args) {

        int num = args[0].toString() == null ? 1 : Integer.parseInt(args[0].toString());
        System.out.println("args[0] is " + num);
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(num);

        final AtomicLong atomic = new AtomicLong(0);
        for (int i = 0; i < num; i++) {

            fixedThreadPool.execute(() -> {
                while (true) {
                    String str = Thread.currentThread().getName();
                    atomic.incrementAndGet();
                    if (atomic.get() > 999999) {
                        System.out.println("============>" + str);
                        atomic.set(0);
                    }
                }
            });
        }

    }
}

htop 或者top -Hp pid查看哪些线程cpu使用率较高

找到cpu使用率较高的线程id后,转成16进制

superuser@t1-test-004:/app/data/jvmtest$ jstack 5149获取进程中线程调用栈信息,并根据上一步的16进制找到对应的线程调用栈信息

你可能感兴趣的:(性能测试)