计算机中每个操作系统给每个进程的内存是有限制的(windows64位系统中最大分配内存是2G),即对于虚拟机,最大内存是2G,不算虚拟机进程启动时所占用的内存,剩下的内存分配给共享区和线程独享区,共享区的内存大小由虚拟机参数(-Xmx最大堆内存,MaxPermSize最大方法区容量)指定,剩下的则分配给栈空间,程序计数器占用的空间极小可忽略不计,每个线程分配到的栈容量越大,可以建立的线程的数量自然就越少,建立线程时则更容易将内存耗尽。
测试:
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.println("stack length:" + oom.stackLength);
throw e;
}
}
}
设置虚拟机参数:
-Xss128k :设置每个线程的栈大小,减小这个值,可以使虚拟机创建更多线程
运行结果:
stack length:967
Exception in thread "main" java.lang.StackOverflowError
at com.example.stackoverflowerror.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:11)
at com.example.stackoverflowerror.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at com.example.stackoverflowerror.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at com.example.stackoverflowerror.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at com.example.stackoverflowerror.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at com.example.stackoverflowerror.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at com.example.stackoverflowerror.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at com.example.stackoverflowerror.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
...
报出虚拟机栈超出虚拟机允许的最大深度的错误(StackOverflowError)。
通过-Xss控制了每个线程的最大栈大小,当栈不断增大时,会暴露栈空间不足的错误,这种错误有两种,一种是上述的栈深度超出,个人理解应该跟数组的outOfIndexError差不多的意思。
另一种就是虚拟机为栈分配的空间不足了,造成内存溢出。
代码:
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 javaVMStackOOM = new JavaVMStackOOM();
javaVMStackOOM.stackLeakByThread();
}
}
设置虚拟机参数:
-Xss2m 将每个线程分配的空间调大一些
由于java线程是映射到操作系统的内核线程上的,因此上述代码执行有较大风险,可能会导致操作系统假死。本人测试的时候就死机了。。。