zoukankan      html  css  js  c++  java
  • 第六篇 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();
        }
    }
    • java.lang.OutofMemoryError异常:

      (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();
        }
    }
  • 相关阅读:
    php 获取文件的md5
    php 获取远程文件大小
    chrome 浏览器,大屏显示
    Mac 中Java项目打包上线
    如何在苹果M1芯片 (Apple Silicon) 上安装 JDK 环境
    Mysql 替换数据中的部分内容,比如迁移服务器,需要修改图片地址
    docker安装指定版本minio
    docker 查询镜像并删除
    docker 容器名称已存在
    docker 安装minio
  • 原文地址:https://www.cnblogs.com/zhexuejun/p/15480505.html
Copyright © 2011-2022 走看看