一、通过运行时验证Java虚拟机规范中描述的各个运行时区域存储的内容
二、在工作中遇到OOM,根据异常信息快速判断是哪个区域的内存溢出,知道什么样的代码可能导致OOM,以及解决方案
Java堆溢出
-
VM参数: Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=D:\Java\dump2.hprof -
代码
Map<String, Long> map = new HashMap<>();
for (long i = 0; i < Long.MAX_VALUE; i++) {
map.put(i + "", i);
}
-
异常
异常堆栈信息会报OutOFMemoryError,紧跟着进一步提示"Java Heap Space" -
解决方案
一般首先通过映像分析工具(MAT)对Dump出来的堆转存储快照进行分析
虚拟机栈和本地方法栈溢出
- HotSpot并不分虚拟机和本地栈,栈容量由-Xss参数设置(递归最容易栈溢出)
- 虚拟机栈描述了两种异常:
- 如果线程请求的的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError
- 如果虚拟机在扩展栈是无法申请到足够的内存空间,则抛出OoutofMemoryError
-
VM参数:-Xss128k
-
代码
class JavaVMStackSOF{
private int stackLength=1;
public void stackLeak(){
stackLength++;
stackLeak();
}
}
JavaVMStackSOF javaVMStackSOF = new JavaVMStackSOF();
while (true){
javaVMStackSOF.stackLeak();
}
-
异常信息
Exception in thread “main” java.lang.StackOverflowError -
异常信息
方法区和运行时常量池溢出()
- 常量池是方法区的一部分(1.7之后会逐步去“永久代”,以下代码1.6之前)
- VM参数:-XX:PermSize=10M -XX:MaxPermSize=10M
- 代码
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create();
}
}
static class OOMObject {
}
本机直接内存溢出
- DirectMemory容量通过-XX:MaxDirectMemorySize指定,如果不指定,默认与Java堆最大值(-Xmx)一样
- VM参数: -Xmx20M -XX:MaxDirectMemorySize=10M
- 代码
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class Dump {
private static final int MB = 1024 * 1024;
public static void main(String[] args) throws IllegalAccessException {
Field field= Unsafe.class.getDeclaredFields()[0];
field.setAccessible(true);
Unsafe un= (Unsafe) field.get(null);
while (true){
un.allocateMemory(MB);
}
}
}
- 异常