1.堆溢出
Java堆的唯一作用就是存储对象实例的数据,只要我们不断的创建新的对象并保证这些新的对象不被Gc回收,当对象数量达到一定容量超过堆的最大容量就会产生内存溢出的异常。
测试代码如下:
package JVM;
import java.util.ArrayList;
import java.util.List;
/**
* Created by louyuting on 17/1/1.
* 测试内容:堆溢出
* 虚拟机参数:-Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {
public static void main(String[] args) {
List<HeapOOM> list = new ArrayList<>();
while (true){
list.add(new HeapOOM());
}
}
}
运行结果:
这种异常很常见,也很好发现,因为都提示了“Java heap space”了,定位问题的话,根据异常堆栈分析就好了,行号都有指示。解决方案的话,可以调大堆的大小或者从代码上检视是否存在某些对象生命周期过长、持有状态时间过长的情况,长时间少程序运行期间的内存消耗。
2.虚拟机栈溢出(本地方法栈溢出)
在Hotspot虚拟机中不区分虚拟机栈和本地方法栈。在虚拟机规范中描述了两种异常:
1. 如果线程请求的栈深度大于虚拟机所允许的最大深度将抛出StackOverflowError异常。
2. 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
这里其实有一些重叠的地方:当栈空间无法继续分配时,到底是内存太小还是已使用栈空间太大,本质是对同一事件的两种描述罢了。
测试代码如下:
package JVM;
/**
* Created by louyuting on 17/1/1.
* 测试内容:栈溢出测试(递归调用导致栈深度不断增加)
* 虚拟机参数:-Xss256k(在JDK1.7要求最小都是160k)
*/
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) {
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch (Throwable e){
System.out.print("stack length :" + oom.stackLength);
throw e;
}
}
}
运行结果:
实际上,在单线程下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配时都抛出的是StackOverflowError。
通过不断创建线程的方式可以产生OutOfMemoryError,因为每个线程都有自己的栈空间。不过这个操作有危险就不做了,可能造成操作系统假死。每个线程分配到的栈容量越大,可以建立的线程数量自然就越少,建立线程时就越容易把内存打爆。