虚拟机参数
- 虚拟机栈:-Xss128k,表示虚拟机栈最大内存为128k。
- 本地方法栈:-Xoss128k,表示本地方法栈最大内存为128k。
由于在HotSpot虚拟机中不区分虚拟机栈和本地方法栈,所以栈容量只由-Xss参数设定。
Java虚拟机规范中描述的两种异常
- 如果线程请求栈深度大于虚拟机所允许最大深度(没有内存供继续分配),将抛出StackOverflowError异常。
- 如果虚拟机在扩展栈时无法申请到足够内存,将抛出OutOfMemoryError异常。
单线程SOF测试
概述
虚拟机栈由栈帧构成,每调用一次方法,将需要在虚拟机栈占用一块内存用于创建一个栈帧,当达到容量上限时,将不能创建栈帧,所以再次调用方法时会抛出异常。
代码清单
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak(){
stackLength++;
stackLeak(); // 迭代调用方法,创建栈帧,占用虚拟机栈的内存
}
public static void main(String[] args) throws Throwable{
JavaVMStackSOF sof = new JavaVMStackSOF();
try{
sof.stackLeak();
}catch (Throwable e){
System.out.println("stack length: " + sof.stackLength);
throw e;
}
}
}
VM Options参数设置
运行结果
循环创建线程导致OOM异常
概述
在单线程程序中,无法出现OOM异常;但是通过循环创建线程(线程体调用方法),的放肆可以产生OOM异常。此时OOM异常产生的原因与栈空间是否足够大无关。
出现OOM的原因是,操作系统分配给每个虚拟机进程的内存是有限制的,比如32位Windows限制为2GB,除去所有线程共享的堆内存和方法区,每开辟一个线程,都需要为其分配一定的栈内存和程序计数器(比重很小)。所以当线程数目达到一定数量时,内存耗尽,此时再有新线程启动时,会抛出OOM异常,因为没有内存分配给它。至于栈空间的容量设置影响到的是开辟的线程数量,栈空间容量越大,能够开辟的线程个数就越少。
代码清单
public class JavaVMStackOOM {
private void dontStop(){
while (true){
}
}
// 循环创建线程,直到内存耗尽,抛出OOM异常
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();
}
}
VM Options参数设置
VM Options:-Xss2M // 设置大一些,每个线程需要的内存多,能够创建的线程数量少
运行结果
运行结果已清晰提示:无法继续创建线程
Exception in Thread "main" java.lang.OutOfMemoryError: unable to create new native thread
总结
- 如果使用虚拟机默认参数,栈深度到达1000~2000完全没问题。大多数情况下,对于方法的正常调用,包括递归,这个深度都够用。
- 如果是建立多线程导致的内存溢出,而且不能减少线程数或者更换为64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。