服务端性能问题排查及优化 ---内存问题分析
临近双11又开始新一轮的性能测试,陆续给大家奉上性能测试系列篇,从性能测试理论、性能测试案例高延迟(响应时间长)、CPU问题、内存问题、线上问题实战和性能测试书籍推荐等篇章。
本篇文章主要对内存问题,从内存溢出发生原因、问题分析过程、实践案例等进行分析和讲解。更多性能文章见文末链接。
- 1 -
内存问题概述
内存溢出
首先,在发生内存溢出的时候,一定要第一时间抓内存的快照,有助于问题的分析。
当内存溢出时需要先分析当前内存配置是否合理,而不是直接增大内存配置。一个无状态的服务,在服务启动和运行一段时间后,内存不会有太大的浮动。
一个有状态的服务,需要在开始阶段计算需要缓存的数据,并配置合理的内存大小。
垃圾回收问题 - 待补充。
Java GC方式和参数有很多,常用的有以下几种模式。-XX:+UseParallelOldGC
FGC时会停顿比较长的时间。-XX:+UseConcMarkSweepGC
FGC停顿时间短,目前生产环境大多使用此配置。-XX:+UseG1GC
JVM参数配置的不合理导致的内存问题 - 待补充
比如配置的太小或者太大,每个区的比例等。
- 2 -
导致内存溢出的原因
| 内存配置过小或者不合理
| 对象没有释放
- 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,加载成功后切换到类的标签,并按实例数量倒序排列,类似以下
通过对应用代码的了解后,发现MemoryLeak数量异常。
点击MemoryLeak展开所有的MemoryLeak对象,通过右下角的引用查找对象的根节点,同时结合代码去分析。
- 3-
内存泄漏示例
| Demo 代码
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分析过程
· 推 荐 阅 读 ·RECOMMENDATION
服务端性能问题排查及优化---CPU高问题分析