在java 虚拟机规范中描述了两种异常:
1.如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError异常
2.如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError
实例一: java堆溢出
package com.com.atguigu;
import java.util.ArrayList;
import java.util.List;
//java 内存溢出实例
public class HeapOOM {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while (true) {
//这里不断的在堆中创建新的实例对象,把内存撑破了
list.add(new OOMObject());
}
}
}
异常信息:
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:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at com.com.atguigu.HeapOOM.main(HeapOOM.java:15)
实例二:java 虚拟机栈和本地方法栈溢出
虚拟机栈溢出
package com.atguigu.newproject;
/**
* VM Args: Xss2M
*/
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
System.out.println("执行次数 " + stackLength);
stackLeak();
}
public static void main(String[] args) {
JavaVMStackSOF me = new JavaVMStackSOF();
try {
//每次调用stackLeak() 方法都会往虚拟机栈中压入一个栈帧,无限递归,就会一直进行压栈操作
//这样某个时间栈的深度会大于会大于虚拟机栈允许的最大深度
me.stackLeak();
} catch (Exception e) {
System.out.println("stack length :" + me.stackLength);
// TODO Auto-generated catch block
throw e;
}
}
}
创建线程导致内存溢出:
package com.com.atguigu;
public class JavaVMStackOOM {
private void dontStop() {
while (true){
}
}
public void StackLeakByThread() {
while (true) {
Thread thread = new Thread(new Runnable() {
public void run() {
dontStop();
}
});
thread.start();
}
}
public static void main(String[] args) {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.StackLeakByThread();
}
}
注意:上面的代码运行时,由于windows平台的虚拟机中,java的线程是映射到操作系统的内核线程上的,所以上述代码执行时有较大风险,可能会导致操作系统假死。
实例三:运行时常量池导致的内存溢出异常
package com.com.atguigu;
import java.util.ArrayList;
import java.util.List;
/**
* VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
*/
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
//使用List保持着常量池引用,避免Full GC 回收常量
List<String> list = new ArrayList<String>();
//10MB 的PermSize 在integer 范围内足够产生OOM了
int i = 0;
while (true) {
//itern() 方法表示如果常量池中已经包含了一个等于此String对象的字符串,则返回池中这个串的String对象,否则, //将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
list.add(String.valueOf(i++).intern());
}
}
}
注意: 从运行结果可以看到,运行时常量池溢出,在OutOfMemoryError 后面跟随的提示信息是“PermGen space”,说明运行时常量池属于方法区的一部分。
查看虚拟机设置:
C:\Users\gezongyang>Java -XX:PermSize=10M -version
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10M; support was removed in 8.0
java version "1.8.0_71"
Java(TM) SE Runtime Environment (build 1.8.0_71-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.71-b15, mixed mode)
这里显示,PermSize=10M; support was removed in 8.0 这种设置已经在java8中不能生效了
实例四:方法区内存溢出
注意:-XX:PermSize=10M -XX:MaxPermSize=10M 这两个参数java8已经不支持了
package com.com.atguigu;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
*/
public class JavaMethodAreaOOM {
public static void main(final String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(objects, args);
}
});
}
}
static class OOMObject {
}
}
以上使用了CGlib包,依赖如下
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
原因分析:一个类如果要被垃圾回收器回收掉,判定条件十分苛刻。这里使用CGlib字节码增强,动态生成大量Class。
实例五:本机直接内存溢出
package com.com.atguigu;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* VM Args: -Xmx20m -XX:MaxDirectMemorySize=10M
*/
public class DirectMemoryOOM {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) throws IllegalAccessException {
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.com.atguigu.DirectMemoryOOM.main(DirectMemoryOOM.java:19)