StackOverflowError的分析和理解

1. 在java虚拟机规范中,定义了在虚拟机栈和本地方法栈中会产生StackOverflowError

2. 虚拟机栈和本地方法栈一般就是我们说的java内存管理中的栈

3. 虚拟机栈和本地方法栈是线程之间的独立内存,每一个线程在创建时,java虚拟机都会给该线程分配一块独立的内存区域,一般将此内存区域划分为虚拟机栈,本地方法栈,程序计数器

4. 虚拟机栈中存储了方法执行时相关信息,每个方法在调用时都会在虚拟机栈中创建一个方法帧,方法帧中包含了局部变量,操作数,动态链接,方法出口等信息

5. 本地方法栈和虚拟机栈基本相同,不同的是本地方法栈是针对线程中的native方法

6. 程序计数器包含了一个程序执行指针,指向了字节码当前执行的行数

7. 在java虚拟机规范中,虚拟机栈和本地方法栈都会出现StackOverflowError和OutofMemoryError,程序计数器是java虚拟机中唯一一块不会产生error的内存区域

8. StackOverflowError代表的是,当栈深度超过虚拟机分配给线程的栈大小时就会出现此error

9. OutofMemoryError代表的是,当再申请新的内存时,虚拟机分配给线程的内存大小中无法再分配新的内存,就会出现此error

10. -Xss1024M虚拟机参数可以设置虚拟机分配给每个线程的内存大小,程序计数器占很小的内存(可以忽略),一般此内存和线程栈内存相等

11. 在HotSpot虚拟机中,是将虚拟机栈和本地方法栈合二为一的


 

以下测试结构都是在HotSpot虚拟机中进行的:

1. 在单线程操作中,无论是栈深度无限增加,还是栈帧(每个方法调用执行时都会在栈中创建一个栈帧,用来存储局部变量,操作数栈,动态链表,方法出口等信息)占的空间太大,都出现的是StackOverflowError

2. 不断创建新的线程的实践中会出现OutofMemoryError的错误

测试一:无限增加栈深度(使用了-Xss1024M参数指定了线程栈大小为1G)

 1 public class StackOverflowErrorMain {
 2     int stackLength = 0;
 3 
 4     public StackOverflowErrorMain() {
 5     }
 6 
 7     public void addStackLength(){
 8         stackLength++;
 9         addStackLength();
10     }
11 
12     public static void main(String[] args){
13         StackOverflowErrorMain sofem = new StackOverflowErrorMain();
14         try {
15             sofem.addStackLength();
16         }catch (Throwable e){
17             System.out.println(sofem.stackLength);
18             e.printStackTrace();
19         }
20     }
21 }

 输出:

66961075
java.lang.StackOverflowError
    at com.mrlu.stackError.StackOverflowErrorMain.addStackLength(StackOverflowErrorMain.java:14)
    at com.mrlu.stackError.StackOverflowErrorMain.addStackLength(StackOverflowErrorMain.java:14)
    at com.mrlu.stackError.StackOverflowErrorMain.addStackLength(StackOverflowErrorMain.java:14)
    at com.mrlu.stackError.StackOverflowErrorMain.addStackLength(StackOverflowErrorMain.java:14)
    at com.mrlu.stackError.StackOverflowErrorMain.addStackLength(StackOverflowErrorMain.java:14)
...

 

测试二:栈帧无限扩大(HotSpot 64位虚拟机要求最小栈大小为160K)

 

测试三:不断创建新的线程:

public class StackOverflowErrorMain {
    int threadCount = 0;

    public StackOverflowErrorMain() {
    }

    public void addNewThread(){
        while (true) {
            threadCount++; //线程的数量
            new Thread() {
                @Override
                public void run() {
                    while (true) ; //线程执行不能停
                }
            }.start();
        }
    }

    public static void main(String[] args){
        StackOverflowErrorMain sofem = new StackOverflowErrorMain();
        try {
            sofem.addNewThread();
        }catch (Throwable e){
            System.out.println(sofem.threadCount);
            e.printStackTrace();
        }
    }
}

 

输出:

 

不断创建新线程产生OutofMemoryError的问题总结:

  操作系统对每个进程分配的内存有大小限制(windows64位系统中最大是2G内存),对于java虚拟机,最大是2G的内存,不算java虚拟机进程启动时占的内存,剩下的内存都分配给了共享区和线程独享区,也就是堆内存+方法区内存+线程栈内存(程序计数器忽略不及),那么不论堆内存和方法区内存占的内存空间有多小,线程栈内存的大小总是有限制的,由于java虚拟机会为每个线程分配一块内存,如果线程数量足够多,当再申请新的内存时,发现无内存可用了,就会抛出OutofMemoryError


 

问题:

1. -Xss1024M代表的是每个线程在创建启动时都会分配这个固定大小的内存吗,还是说这个内存是这个线程栈内存的最大值?

2. linux中每个进程内存大小的限制是多少?

3. 如何实时查看一个进程占内存的大小?(top?)

4. 如何实时查看jvm虚拟机中每个线程占内存的大小?


 

参考书籍:[(深入理解JAVA虚拟机)]

posted @ 2016-04-26 10:26  桦沐  阅读(43736)  评论(3编辑  收藏