zoukankan      html  css  js  c++  java
  • JVM虚拟机栈简介

    1.简介

    1.1 虚拟机栈的出现背景

    • 由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的【如果设计成基于寄存器的,耦合度高,性能会有所提升,因为可以对具体的CPU架构进行优化,但是跨平台性大大降低】。
    • 它的优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。

    1.2 什么是Java虚拟机栈

    Java虚拟机栈(Java Virtual Machine Stack),早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的Java方法调用,栈是线程私有的,是一个后入先出的栈。
    在这里插入图片描述

    1.3 虚拟机栈的生命周期

    • 生命周期和线程一致,也就是线程结束了,该虚拟机栈也销毁了

    1.4 虚拟机栈的作用

    • 主管Java程序的运行,它保存方法的局部变量(8 种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回。
    • 局部变量,它是相比于成员变量来说的(或属性)
    • 基本数据类型变量 VS 引用类型变量(类、数组、接口)

    1.5 虚拟机栈的特点

    • 栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器。
    • JVM直接对Java栈的操作只有两个:
      • 每个方法执行,伴随着进栈(入栈、压栈)
      • 执行结束后的出栈工作
    • 对于栈来说不存在垃圾回收问题
      • 栈不需要GC,但是可能存在OOM(OutOfMemory 内存溢出)

    1.6 虚拟机栈的异常

    Java 虚拟机规范允许Java栈的大小是动态的或者是固定不变的。

    • 如果采用固定大小的Java虚拟机栈,那每一个线程的Java虚拟机栈容量可以在线程创建的时候独立选定。如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量,Java虚拟机将会抛出一个StackoverflowError(栈溢出) 异常。
    • 如果Java虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那Java虚拟机将会抛出一个 OutofMemoryError(内存溢出) 异常。

    1.7 设置栈内存大小

    可以使用参数 -Xss 选项来设置线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度。

    设置线程栈大小,可以以字节表示,以字母k或K表示KB,以字母m或M表示MB,以字母g或G表示GB。

    例如:
    -Xss1m
    -Xss1024k
    -Xss1048576
    都是设置为1M大小

    而默认值取决于平台:

    • Linux/x64 (64-bit): 1024 KB
    • macOS (64-bit): 1024 KB
    • Oracle Solaris/x64 (64-bit): 1024 KB
    • Windows: 默认值取决于虚拟内存

    举例来说:

    public class Example {
        private static int count = 1;
        
        public static void main(String args[]) {
            System.out.println(count);
            count++;
            main(args);
        }
    }
    

    未设置参数前输出结果:

    11403
    11404
    11405
    11406
    Exception in thread "main" java.lang.StackOverflowError
    	at sun.nio.cs.ext.DoubleByte$Encoder.encodeLoop(DoubleByte.java:617)
    	at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)
    

    可以看到在栈在11406的深度溢出了。

    而设置参数后:
    在这里插入图片描述
    输出结果:

    2462
    2463
    2464
    2465
    Exception in thread "main" java.lang.StackOverflowError
    	at sun.nio.cs.ext.DoubleByte$Encoder.encodeArrayLoop(DoubleByte.java:578)
    	at sun.nio.cs.ext.DoubleByte$Encoder.encodeLoop(DoubleByte.java:617)
    

    说明设置的参数起作用了。

    2.栈的存储单位(栈帧)

    2.1 栈中存储什么

    • 每个线程都有自己的栈,栈中的数据都是以栈帧(Stack Frame)的格式存在
    • 在这个线程上正在执行的每个方法都各自对应一个栈帧(Stack Frame)。
    • 栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息。

    2.2 栈运行原理

    • JVM直接对Java栈的操作只有两个,就是对栈帧的压栈和出栈,遵循先进后出(后进先出)原则
    • 在一条活动线程中,一个时间点上,只会有一个活动的栈帧。即只有当前正在执行的方法的栈帧(栈顶栈帧)是有效的。这个栈帧被称为当前栈帧(Current Frame),与当前栈帧相对应的方法就是当前方法(Current Method),定义这个方法的类就是当前类(Current Class)
    • 执行引擎运行的所有字节码指令只针对当前栈帧进行操作。
    • 如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,放在栈的顶端,成为新的当前帧。
      在这里插入图片描述
      注意:
    • 不同线程中所包含的栈帧是不允许存在相互引用的,即不可能在一个栈帧之中引用另外一个线程的栈帧。
    • 如果当前方法调用了其他方法,方法返回之际,当前栈帧会传回此方法的执行结果给前一个栈帧,接着,虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧。
    • Java方法有两种返回函数的方式。
      • 一种是正常的函数返回,使用return指令。
      • 另一种是方法执行中出现未捕获处理的异常,以抛出异常的方式结束。
      • 但不管使用哪种方式,都会导致栈帧被弹出。
  • 相关阅读:
    366. Find Leaves of Binary Tree输出层数相同的叶子节点
    716. Max Stack实现一个最大stack
    515. Find Largest Value in Each Tree Row查找一行中的最大值
    364. Nested List Weight Sum II 大小反向的括号加权求和
    156. Binary Tree Upside Down反转二叉树
    698. Partition to K Equal Sum Subsets 数组分成和相同的k组
    244. Shortest Word Distance II 实现数组中的最短距离单词
    187. Repeated DNA Sequences重复的DNA子串序列
    java之hibernate之基于主键的双向一对一关联映射
    java之hibernate之基于主键的单向一对一关联映射
  • 原文地址:https://www.cnblogs.com/niulongwei/p/14864491.html
Copyright © 2011-2022 走看看