堆内存溢出
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
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的堆内存快照
由下面信息定位到问题代码出现在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
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
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)