概述
內存問題主要體現在以下幾個方面
- 內存溢出
- 內存各區比例的配置不合理
- 垃圾回收方式不合理
本文主要介紹內存溢出的排查方法。
-
內存溢出
首先,在發生內存溢出的時候,一定要第一時間抓內存的快照,有助於問題的分析。
當內存溢出時需要先分析當前內存配置是否合理,而不是直接增大內存配置。- 一個無狀態的服務,在服務啓動和運行一段時間後,內存不會有太大的浮動。
- 一個有狀態的服務,需要在開始階段計算需要緩存的數據,並配置合理的內存大小。
垃圾回收問題 - 待補充。
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區回收。
可能導致內存溢出的情況
內存配置過小或者不合理
內存配置過小確實是內存溢出的一個原因,每個開發都應在應用上線前估算內存使用的一個大概數量級別,根據估算結果去配置一個合理的內存閾值(閾值?理會精神..),同時多關注內存的一個趨勢,避免因爲配置原因導致內存溢出。對象沒有釋放
比較常見的問題,項目中經常出現多個對象的循環引用,最後某一個環節的對象hold住了所有的這些沒用的對象,導致垃圾回收不能及時的回收,進而導致內存溢出。
分析方法
首先保證出現問題的時候有東西去分析,可通過以下方法去保留事發現場。
- 通過增加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,加載成功後切換到類的標籤,並按實例數量倒序排列,類似以下
通常情況下,實例最多的爲基礎類型,比如byte、string、int等。儘量先從本項目的類來分析。
通過對應用代碼的瞭解後,發現MemoryLeak數量異常。
- 點擊MemoryLeak展開所有的MemoryLeak對象,通過右下角的引用查找對象的根節點,同時結合代碼去分析。
內存泄漏示例
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