1.8 内存溢出实战

时间:2021-12-24 01:03:40


堆内存溢出

1.代码示例

public class HeapOOMDemo {
private List<String> oomList = new ArrayList<>();

public static void main(String[] args) {
HeapOOMDemo heapOOMDemo = new HeapOOMDemo();
while (true) {
heapOOMDemo.oomList.add(UUID.randomUUID().toString());
}
}
}

2.设置堆最小和最大内存并让应用在堆内存溢出的时候,进行一次堆Dump

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

1.8 内存溢出实战

3.执行程序,输出如下结果

java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to java_pid1416.hprof ...
Heap dump file created [21291311 bytes in 0.084 secs]
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.Arrays.copyOfRange(Arrays.java:3664)
at java.lang.String.<init>(String.java:207)
at java.lang.StringBuilder.toString(StringBuilder.java:407)
at java.util.UUID.toString(UUID.java:380)
at com.steven.HeapOOMDemo.main(HeapOOMDemo.java:14)

4.使用jvisualvm载入Dump的堆内存快照

1.8 内存溢出实战


由下面信息定位到问题代码出现在HeapOOMDemo.java的14行。

"main" prio=5 tid=1 RUNNABLE
at java.lang.OutOfMemoryError.<init>(OutOfMemoryError.java:48)
at java.util.Arrays.copyOfRange(Arrays.java:3664)
Local Variable: char[]#32744
at java.lang.String.<init>(String.java:207)
at java.lang.StringBuilder.toString(StringBuilder.java:407)
at java.util.UUID.toString(UUID.java:380)
at com.steven.HeapOOMDemo.main(HeapOOMDemo.java:14)
Local Variable: com.steven.HeapOOMDemo#1

栈内存溢出

1.代码示例

public class StackOOMDemo {
private int length = 0;

private void stackLeak() {
this.length++;
this.stackLeak();
}

public static void main(String[] args) {
StackOOMDemo stackOOMDemo = new StackOOMDemo();
try {
stackOOMDemo.stackLeak();
} catch (Exception e) {
throw e;
} finally {
System.out.println("stack length:" + stackOOMDemo.length);
}
}
}

在stackLeak方法里定义一个局部变量字符串数组。

public class StackOOMDemo {
private int length = 0;

private void stackLeak() {
String [] strArr = {"one","two","three","four","five","six","steven","eight","nine","ten"};
this.length++;
this.stackLeak();
}

public static void main(String[] args) {
StackOOMDemo stackOOMDemo = new StackOOMDemo();
try {
stackOOMDemo.stackLeak();
} catch (Exception e) {
throw e;
} finally {
System.out.println("stack length:" + stackOOMDemo.length);
}
}
}

2.设置栈最小内存

-Xss1024k

1.8 内存溢出实战

3.分别执行程序,输出如下结果

stack length:1889
Exception in thread "main" java.lang.*Error
at com.steven.StackOOMDemo.stackLeak(StackOOMDemo.java:7)
at com.steven.StackOOMDemo.stackLeak(StackOOMDemo.java:8)
at com.steven.StackOOMDemo.stackLeak(StackOOMDemo.java:8)
at com.steven.StackOOMDemo.stackLeak(StackOOMDemo.java:8)
at com.steven.StackOOMDemo.stackLeak(StackOOMDemo.java:8)
//......
stack length:1732
Exception in thread "main" java.lang.*Error
at com.steven.StackOOMDemo.stackLeak(StackOOMDemo.java:7)
at com.steven.StackOOMDemo.stackLeak(StackOOMDemo.java:8)
at com.steven.StackOOMDemo.stackLeak(StackOOMDemo.java:8)
at com.steven.StackOOMDemo.stackLeak(StackOOMDemo.java:8)
at com.steven.StackOOMDemo.stackLeak(StackOOMDemo.java:8)
//......

4.分析
从上述输出结果可以看到,相同配置,申明局部变量字符串数组的代码要少执行157次stackLeak()方法。这是因为,JVM在执行方法的时候,会为方法创建一个栈帧,栈帧存放方法的局部变量、操作数栈、返回值等信息,然后压入栈,而局部变量会占据一定的内存空间,且没有被释放,因此会导致执行方法的次数减少。

元空间内存溢出

1.代码示例

public class MethodAreaOOMDemo {
public static void main(String[] args) {
Set<String> stringSet = new HashSet<>();
int count = 1;
while (true) {
//intern()是一个本地方法
//如果字符串常量池存在需要加入的值,则直接返回字符串常量池中的引用
//如果字符串常量池不存在需要加入的值,则将该字符串加入字符串常量池并返回引用
stringSet.add(String.valueOf(count).intern());
}
}
}

2.设置元空间初始值和最大值

-XX:MetaspaceSize=2m -XX:MaxMetaspaceSize=2m

1.8 内存溢出实战

3.分别执行程序,输出如下结果

Error occurred during initialization of VM
java.lang.OutOfMemoryError: Metaspace
at sun.misc.Launcher.<init>(Launcher.java:67)
at sun.misc.Launcher.<clinit>(Launcher.java:53)
at java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1451)
at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1436)