JVM学习笔记-虚拟机栈和本地方法栈溢出

     关于虚拟机栈和本地方法栈,在JAVA虚拟机规范中描述了两种异常:

1.如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常:个人感觉应该分为栈深度导致,及该线程所用栈内空间溢出所致。

2.如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

 

上述虽把异常分2种,但需要注意:当栈空间无法继续分配时,到底是内存太小,还是已使用的栈空间(容量)太大,其本质上只是对同一件事情的2种描述而已。

在单线程的情况下,很难让虚拟机产生OutOfMemoryError异常,无论是由于栈桢太大,还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackOverflowError异常。

 

如果是在多线程的情况下,通过不断地建立线程的方式倒是可以产生内存溢出异常,但是,这样产生的内存溢出与栈空间是否足够大并不存在任何关系,或者准确地说,在这种情况下,给每个线程的栈分配的内存越大,反而越容易产生内存异常。其原因也很容易理解,操作系统分配给每个进程的内存是有限制的,譬如32位的windows限制是2GB。虚拟机提供了参数来控制java堆和方法区的这两部分内存的最大值。剩余的内存为2GB(操作系统限制) - Xmx(最大堆容量) - MaxPermSize(最大方法区容量),程序计数器消耗内存很小,可以忽略掉。如果虚拟机进程本身耗费的内存不计算在内,剩下的内存就由虚拟机栈和本地方法栈“瓜分”了。每个线程分配到的栈容量越大,可以建立的线程数量自然就越少,建立线程时就越容易把剩下的内存耗尽。

       这一点对于开发多线程应用时应该注意,出现StackOverflowError异常时有错误堆栈可以阅读,相对来说,比较容易找到问题的所在。而且,如果使用虚拟机默认参数,栈深度在大多数情况下达到1000~2000完全没有问题,对于正常的方法调用(包括递归),这个深度应该完全够用了。但是,如果是建立过多的线程导致的内存溢出,在不能减少线程数或者更换64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。如果没有这方面的经验,这种通过“减少内存”的手段来解决内存溢出的方式会比较难以想到。

阅读更多
个人分类: Java
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭