微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

第六篇 JVM之运行时数据区<2>: 虚拟机栈

  虚拟机栈是线程执行Java程序时,处理Java方法内容的内存区域。虚拟机栈也是线程私有的区域,每个Java方法调用的时候,都会在虚拟机栈中创建出一个栈帧。如图,栈帧又由局部变量表、操作数栈、动态链接方法返回四部分组成,有些虚拟机的栈帧还包括了一些附加信息。


一、虚拟机栈运行原理

  如图,线程执行Java方法时,从Java方法1开始依次调用,每调用一个方法,就会有入栈操作,向虚拟机栈中压入一个栈帧,线程当前正在执行的Java方法对应了栈顶栈帧(当前栈帧),当前方法执行完成后,当前方法的执行结果传给下一个栈帧,之后进行出栈操作,丢弃当前栈帧(无论方法有没有产生异常,都会丢弃),下一个栈帧就会称为新的栈顶栈帧,从之前调用已执行完方法的位置继续执行。虚拟机栈是线程的私有区域,并且栈帧不允许被其他线程访问,所以不存在线程安全问题,栈帧弹出后就内存就会被系统回收,所以不也存在垃圾回收问题。

 栈结构处理Java方法的优势:

  • 1、速度快,栈分配存储的速度仅次于程序计数器。
  • 2、操作简便,线程执行Java方法时,只有入栈和出栈操作,调用时入栈,执行结束时出栈。
  • 3、不存在垃圾回收问题。

二、虚拟机栈大小设置

1、设置虚拟栈的参数

 虚拟栈的大小可以通过-Xss参数设置,认单位是byte,也可以使用不区分大小写k,m,g作为单位(如-Xss1m),如下不同系统下的-Xss认值不同

  • Linux: 1024k
  • MacOs: 1024k
  • Windows: 认值依赖于虚拟机的内存。

2、虚拟机栈的异常

  • java.lang.StackOverflowError异常:如果采用固定大小的Java虚拟机栈,那每一个线程的Java虚拟机栈容量可以在线程创建的时候独立选定。如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量,Java虚拟机将会抛出StackOverflowError异常,用下面代码可以验证异常。
/**-
 *
 *  JVM参数: -Xss512k
 **/
public class StackOverflowErrorDemo {
    private static int i = 0; // 方法调用次数
    public static void main(String[] args) {
        method1();
    }

    public static void method1() {
        System.out.println(i++);
        method1();
    }
}

  (1)如果Java虚拟机栈允许动态扩展,并且在尝试扩展的时候无法申请到足够的内存,就会抛出OutofMemoryError异常。《Java虚拟机规范》中允许虚拟机自行选择是否支持动态扩展,HotSpot虚拟机选择不支持,所以我们使用的openJDK去用-Xss参数设小栈内存或者创建大量的局部变量的方式去测试时,HotSpot虚拟机抛出的始终是StackOverflowError异常。并且栈容量最小值是有限制的,主要取决操作系统的内存分页。像早期的Classic虚拟机支持动态扩展栈内存,就会出现OOM异常。

  (2)不断创建新的线程,当创建线程时没有足够的内存去创建对应的虚拟机栈,那Java虚拟机将会抛出一个OutofMemoryError异常。原因是操作系统分配给JVM进程的内存是有限的,而这部分内存堆区和方法区占据大部分内存,当方法区和堆区所占内存越大,可供线程用来分配给虚拟机栈的内存就越少,每个线程都有私有的虚拟机栈,不断创建线程,当可用内存耗尽,新的线程无法申请到内存给虚拟机栈,就会出现OutofMemoryError异常。下面的验证代码摘抄自《深入理解Java虚拟机》,对系统压力较大,谨慎执行。

/**
 *
 * VM args: -Xss2M (设置大一些,在32位系统下运行)
 **/
public class JavaVMStackOOM {
    private void dontStop() {
        while (true) {

        }
    }

    private 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();
    }
}

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐