2.4 实战 OutOfMemoryError 异常
面试官:项目中你有没有遇到过Java虚拟机方面的问题,做过虚拟机调优嘛,来,给朕手写一个堆内存溢出的Demo
注:IDEA内存映像分析工具: https://blog.csdn.net/qq_19674905/article/details/80824858
Jprofile exe:https://www.ej-technologies.com/download/jprofiler/version_92
2.4.1 Java堆溢出
import java.util.ArrayList;
/**
* @Description HeapOOM
* @Author Zerah
* @Date 2019/12/20 13:04
* VM args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {
static class OOMObject{
}
public static void main(String[] args) {
ArrayList<OOMObject> list = new ArrayList<>();
while (true){
list.add(new OOMObject());
}
}
/** 运行结果
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid7472.hprof ...
Heap dump file created [28238887 bytes in 0.175 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
**/
}
2.4.2 虚拟机栈和本地方法栈溢出
/**
* @Description JavaVMStackSOF 虚拟机栈和本地方法栈溢出OOM测试
* @Author Zerah
* @Date 2019/12/20 13:35
* VM args: -Xss128k
*/
public class JavaVMStackSOF {
private int stackLength =1;
public void stackLeak(){
stackLength ++;
stackLeak();
}
public static void main(String[] args) throws Throwable{
JavaVMStackSOF stackSOF = new JavaVMStackSOF();
try {
stackSOF.stackLeak();
} catch (Throwable e) {
System.out.println("stack length:"+ stackSOF.stackLength);
throw e;
}
}
/** 运行结果:
* stack length:1611
* Exception in thread "main" java.lang.StackOverflowError
* at com.zerah.concurrent.jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
* at com.zerah.concurrent.jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
* at com.zerah.concurrent.jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
* 省略。。。。。。
*/
}
/**
* @Description JavaVMStackOOM 创建线程导致内存溢出异常, 别跑了,会把机子搞死机,别问我是怎么知道的,非要跑把其他软件能保存得保存下
* @Author Zerah
* @Date 2019/12/23 14:13
* VM args: -Xss2M
*/
public class JavaVMStackOOM {
private void dontStop(){
while (true){
}
}
public void stackLeakByThread(){
while (true){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
dontStop();
}
});
thread.start();
}
}
public static void main(String[] args) {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
2.4.3 方法区和运行时常量池溢出
import java.util.ArrayList;
/**
* @Description RuntimeConstantPoolOOM 运行时常量池导致的内存溢出异常 JDK1.6及之前版本可测试,1.6之前常量池在永久代中分配
* String.intern() 是一个Native方法: 如果字符串常量池中已经包含一个等于此String对象得字符串,则返回常量池中代表这个字符串得【String对象】,
* 否则,将此String对象包含的字符串添加到常量池中,并且返回此【String对象的引用】
*
* 如果使用JDK1.7 + 测试,如果不限制堆内存大小,while循环将一直进行下去,JDK1.7字符串常量池由永久代转移到堆中,JDK1.8之后移除永久代由元空间替代
* 关于元空间的测试可以看:https://blog.csdn.net/qq_16681169/article/details/70471010
* @Author Zerah
* @Date 2019/12/23 14:25
*
* VM args: -XX:PermSize=10M -XX:MaxPermSize=10M
* 如果限制对内存大小:-XX:PermSize=10M -XX:MaxPermSize=10M -Xmx15M
*/
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
// 使用List保持着常量池的引用,避免Full GC回收常量池
ArrayList<String> list = new ArrayList<>();
// 10MB的PermSize在Integer 范围内足够产生OOM了
int i= 0;
while (true){
list.add(String.valueOf(i++).intern());
}
}
}
异常输出
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
at java.util.ArrayList.add(ArrayList.java:462)
at com.zerah.concurrent.jvm.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:23)
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=10M; support was removed in 8.0
import java.lang.reflect.Method;
/**
* @Description JavaMethodAreaOOM 借助CGLib 使方法区出现内存溢出异常 JDK1.6
* @Author Zerah
* @Date 2019/12/23 15:04
* VM args: -XX:PermSize=10M -XX:MaxPermSize=10M
*/
public class JavaMethodAreaOOM {
public static void main(String[] args) {
while (true){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj,args);
}
});
enhancer.create();
}
}
static class OOMObject{}
}
2.4.4 本机直接内存溢出
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* @Description DirectMemorryOOM
* @Author Zerah
* @Date 2019/12/23 15:49
* VM args: -Xmx20M -XX:MaxDirectMemorySize=10M 如果不指定,默认与Java堆最大值(-Xmx)一样
*/
public class DirectMemoryOOM {
private static final int _1MB = 1024*1024;
public static void main(String[] args) throws Exception{
Field field = Unsafe.class.getDeclaredFields()[0];
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
while (true){
unsafe.allocateMemory(_1MB);
}
}
}
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at com.zerah.concurrent.jvm.DirectMemoryOOM.main(DirectMemoryOOM.java:21)
注:本文栗子源自《深入理解Java虚拟机》