除了程序计数器,虚拟机中的其他几个运行时数据区都有发生内存溢出异常的可能。
Java堆的作用是存储对象实例,因此也意味着只要不断的创建对象,并且避免被垃圾回收,就会出现Java堆溢出的异常。
首先在idea中设置JVM参数,在VM options中设置参数,具体步骤按如下:
以下为JVM中配置的参数信息,-verbose:gc 表示打开GC的跟踪日志;-Xms表示最小堆空间,-Xmx表示最大堆空间,-Xmn表示新生代的大小,-XX:+PrintGCTimeStamps表示打印GC发生的时间戳;-XX:SurvivorRatio=8表示新生代中Eden区域和Survivor区域的容量比值
-verbose:gc
-Xms20M
-Xmx20M
-Xmn10M
-XX:+PrintGCDetails
-XX:SurvivorRatio=8
通过一段简单的java代码不断创建对象,当内存超过JVM设置的最大堆空间后就发生堆内存溢出:
public class JavaHeapOOM {
static class OOMproject{
}
public static void main(String[] args){
List<OOMproject> list=new ArrayList<OOMproject>();
while (true){
list.add(new OOMproject());
}
}
}
程序执行后就会因为堆内存溢出而发生报错:
虚拟机栈和本地方法栈用于存放局部变量表、操作栈、动态链接、方法出口等信息,因此可以使用类似递归的方式不断调用方法压入栈中(有数据结构基础的人应该很好理解),让线程请求的栈深度大于虚拟机所允许的最大深度
首先还是设置jvm参数,这里可以使用-Xss128k让虚拟机栈的内存变得很小,接着运行下面的代码:
public class JavaVMStackOF {
private int stackLength=1;
public void stackLeak(){
stackLength++;
stackLeak();
}
public static void main(String[] args)throws Throwable{
JavaVMStackOF oom=new JavaVMStackOF();
try{
oom.stackLeak();
}catch (Throwable e){
System.out.println("stack length = " +oom.stackLength);
throw e;
}
}
}
从结果中可以看出,在调用了991次stackleak()方法后,发生了StackOverFlowError
虚拟机栈和本地方法栈内存还有一种异常叫做OutOfMemoryError,要出现这个异常其实并不容易,其中一个思路就是不断创建线程,并且让每个线程都运行,接着便会发生OutOfMemoryError:
public class JVMStackOOM {
private void dontstop(){
while(true){}
}
public void stackLeakThread(){
while(true){
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
dontstop();
}
});
thread.start();
}
}
public static void main(String[] args){
JVMStackOOM oom=new JVMStackOOM();
oom.stackLeakThread();
}
}
上面的代码可能会导致操作系统假死,请谨慎执行!
方法区存放的是已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,因此想让方法区溢出可以产生大量的类去填满方法区从而导致溢出。产生方法区内存溢出的原因还有大量JSP或动态产生JSP文件的应用等等。