服务端性能问题排查及优化 ---内存问题分析

临近双11又开始新一轮的性能测试,陆续给大家奉上性能测试系列篇,从性能测试理论、性能测试案例高延迟(响应时间长)、CPU问题、内存问题、线上问题实战和性能测试书籍推荐等篇章。

本篇文章主要对内存问题,从内存溢出发生原因、问题分析过程、实践案例等进行分析和讲解。更多性能文章见文末链接。

- 1 - 

 内存问题概述

 本文主要介绍内存溢出的排查方法。

  • 内存溢出
    首先,在发生内存溢出的时候,一定要第一时间抓内存的快照,有助于问题的分析。
    当内存溢出时需要先分析当前内存配置是否合理,而不是直接增大内存配置。

    • 一个无状态的服务,在服务启动和运行一段时间后,内存不会有太大的浮动。

    • 一个有状态的服务,需要在开始阶段计算需要缓存的数据,并配置合理的内存大小。

  • 垃圾回收问题 - 待补充。
    Java GC方式和参数有很多,常用的有以下几种模式。
    -XX:+UseParallelOldGC FGC时会停顿比较长的时间。
    -XX:+UseConcMarkSweepGC FGC停顿时间短,目前生产环境大多使用此配置。
    -XX:+UseG1GC

  • JVM参数配置的不合理导致的内存问题 - 待补充
    比如配置的太小或者太大,每个区的比例等。

高并发的系统中,服务端响应速度对服务本身影响很大,响应时间从1秒增加到2秒会导致内存里的对象的生命周期变长,导致内存增大。生命周期变长还会导致原本在Young区能回收的对象提升到Old区,导致频繁的FGC。

CMS方式可以减少停顿时间,但并不是没有,内存分配不合理还是会导致一些问题,比如业务场景导致大部分对象会进入Old区,Old区分配比较大,频繁的FGC也比较浪费资源,可以考虑重新分配Young区、Old区大小以及Eden:S0:S1比例,使对象尽量的在Young区回收。

- 2 - 

 导致内存溢出的原因

内存配置过小或者不合理

内存配置过小确实是内存溢出的一个原因,每个开发都应在应用上线前估算内存使用的一个大概数量级别,根据估算结果去配置一个合理的内存阈值(阈值?理会精神..),同时多关注内存的一个趋势,避免因为配置原因导致内存溢出。

对象没有释放

比较常见的问题,项目中经常出现多个对象的循环引用,最后某一个环节的对象hold住了所有的这些没用的对象,导致垃圾回收不能及时的回收,进而导致内存溢出。

- 3- 

 内存问题的分析方法

首先保证出现问题的时候有东西去分析,可通过以下方法去保留事发现场。

  • 通过增加jvm启动参数,使内存溢出时自动抓dump
    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/outofmemory.dump

  • 通过jmap命令抓取应用的内存dump,分析内存是否有泄漏,使用介绍如下
    jmap -dump:live,format=b,file=heap.bin <pid>
    详细使用方法可以使用jmap -h查看

分析dump信息
  • 通过jviaualvm加载抓取的dump,加载成功后切换到类的标签,并按实例数量倒序排列,类似以下

    outside_default.png

通常情况下,实例最多的为基础类型,比如byte、string、int等。尽量先从本项目的类来分析。
通过对应用代码的了解后,发现MemoryLeak数量异常。

outside_default.png

  • 点击MemoryLeak展开所有的MemoryLeak对象,通过右下角的引用查找对象的根节点,同时结合代码去分析。

- 3- 

 内存泄漏示例

Demo 代码

MemoryLeak

public class MemoryLeak {
    private long id;
    private Byte[] bytes;

    public MemoryLeak() {
        id = new Date().getTime();
        int count = 256;
        bytes = new Byte[count];
        for (int i = 0; i < count; i++) {
            bytes[i] = (byte) i;
        }
    }
}
TestDemo
package demo.memory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.util.List;

/**
 * VM options -Xms128m -Xmx128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=outofmemory.bin
 *
 * Exception in thread "main" *** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message can't create byte arrau at JPLISAgent.c line: 813
 * java.lang.OutOfMemoryError: Java heap space
 *  at demo.memory.MemoryLeak.<init>(MemoryLeak.java:22)
 *  at demo.memory.TestDemo.main(TestDemo.java:44)
 *
 */
public class TestDemo {
    static MemoryLeak[] memoryLeakArray;
    private static int processId = 0;

    public static void main(String[] args) throws Exception {
        processId = getProcessID();

        MemoryMXBean memorymbean = ManagementFactory.getMemoryMXBean();
        MemoryUsage usage = memorymbean.getHeapMemoryUsage();
        System.out.println("INIT HEAP: " + usage.getInit());
        System.out.println("MAX HEAP: " + usage.getMax());
        System.out.println("USE HEAP: " + usage.getUsed());
        List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
        System.out.println("===================JVM OPTIONS=============== ");
        System.out.println(inputArguments);

        int sec = 30;
        int count = 1024;
        memoryLeakArray = new MemoryLeak[count * sec];
        for (int s = 0; s < sec; s++) {
            for (int i = 0; i < count; i++) {
                memoryLeakArray[s * count + i] = new MemoryLeak();
            }
            Thread.sleep(1000);
            System.out.println("sec:" + s);
            printGcStat();
            //System.in.read();
        }

        while (true) {
            Thread.sleep(1000);
        }

    }

    /**
     * 打印GC性信息
     */
    public static void printGcStat() {
        Process process;
        try {
            process = Runtime.getRuntime().exec("jstat -gcutil " + processId);
            BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = input.readLine()) != null) {
                System.out.println(line);
            }
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取进程ID
     * @return
     */
    public static int getProcessID() {
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        return Integer.valueOf(runtimeMXBean.getName().split("@")[0]);
    }

}
执行结果
demo.memory.TestDemo
INIT HEAP: 134217728
MAX HEAP: 128974848
USE HEAP: 4091336
===================JVM OPTIONS===============
[-Xms128m, -Xmx128m, -XX:+HeapDumpOnOutOfMemoryError, -XX:HeapDumpPath=outofmemory.bin, -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=57241:/Applications/IntelliJ IDEA.app/Contents/bin, -Dfile.encoding=UTF-8]
sec:0
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  44.05   0.00  17.29  19.76      0    0.000     0    0.000    0.000
sec:1
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  70.05   0.00  17.29  19.76      0    0.000     0    0.000    0.000
sec:2
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  96.06   0.00  17.29  19.76      0    0.000     0    0.000    0.000
sec:3
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00  99.53  21.34  25.99  93.32  83.13      1    0.023     0    0.000    0.023
sec:4
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00  99.53  46.59  25.99  93.32  83.13      1    0.023     0    0.000    0.023
sec:5
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00  99.53  69.88  25.99  93.32  83.13      1    0.023     0    0.000    0.023
sec:6
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00  99.53  95.10  25.99  93.32  83.13      1    0.023     0    0.000    0.023
sec:7
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  21.62  69.21  93.33  83.13      2    0.061     1    0.525    0.586
sec:8
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  47.12  69.21  93.33  83.13      2    0.061     1    0.525    0.586
sec:9
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  72.62  69.21  93.33  83.13      2    0.061     1    0.525    0.586
sec:10
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  98.11  69.21  93.33  83.13      2    0.061     1    0.525    0.586
sec:11
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  38.39  99.82  93.37  83.13      2    0.061     2    0.672    0.733
sec:12
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  63.89  99.82  93.37  83.13      2    0.061     2    0.672    0.733
sec:13
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  89.39  99.82  93.37  83.13      2    0.061     2    0.672    0.733
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to outofmemory.bin ...
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
Heap dump file created [205085894 bytes in 1.296 secs]
    at demo.memory.MemoryLeak.<init>(MemoryLeak.java:23)
    at demo.memory.TestDemo.main(TestDemo.java:44)

Process finished with exit code 1
JVisualVM分析过程

b8c7ce93b9424d8f8ece7f30299d71fe.gif

9767bcca976c0544f816f9e0e0eec5b6.jpeg

文章来源:MiniStarClub北京,致力于提供最具价值的测试及测试管理领域原创文章。包括测试技术、测试方法、测试思想、测试管理等。

· 推 荐 阅 读 ·

RECOMMENDATION

服务端性能测试指标及问题排查

服务端性能问题排查及优化---CPU高问题分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值