我们已经知道了JVM的架构主要分为三部分:类加载器,JVM内存结构,执行引擎

JVM内存结构又分为五部分,这节来简单的看看有关虚拟机栈知识点。

 

虚拟机栈:

   定义:

  •   JAVA中线程的运行需要被分配内存空间,而这个内存空间就叫做栈。

   特点:

  1. 线程私有的
  2. 没有垃圾回收机制。
  3. 栈区是由一个个单独的栈(线程栈)组成的。
  4. JVM为每个线程都会创建一个栈(线程栈),用于存放该线程执行的信息(实际参数,局部变量)。
  5. JVM中线程栈的每个方法被调用都会创建一个栈帧(存储局部变量,操作数栈,方法出口)

java空闲线程堆栈 jvm 线程栈_java空闲线程堆栈

 

简要看下虚拟机栈的执行:

       栈是线程私有的,每个线程都是自己的线程栈,每个线程中的每个方法在执行的过程中会创建一个栈帧用于存局部变量表、操作数栈、动态链接、方法返回地址等信息。每一个方法从调用到执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈(进入线程栈)到出栈(离开线程栈)的过程。其中局部变量表,存放基本类型(boolean、byte、char、short、int、float)、对象的引用等等,对象的引用不是对象实例本身,而是指向对象实例的一个指针。

java空闲线程堆栈 jvm 线程栈_操作数_02

java空闲线程堆栈 jvm 线程栈_JVM_03

java空闲线程堆栈 jvm 线程栈_操作数_04

java空闲线程堆栈 jvm 线程栈_局部变量_05

java空闲线程堆栈 jvm 线程栈_操作数_06

解释一下几个重要的特点:

  1. 线程私有的:每个线程都会分配一个线程栈,线程独享一个线程栈,所以是线程私有。
  2. 没有垃圾回收机制:线程栈中的线程操作都是入栈,出栈操作,最后都不会停留在栈中,所以栈不需要垃圾回收机制。

 

栈内存溢出:我们通过上面的介绍了解了什么是虚拟机栈,虚拟机栈在使用的时候可能会存在许多问题,其中最常见的便是

栈内存溢出,所谓的栈内存溢出是指,栈内存不够使用导致出现错误。

  导致栈内存溢出的两种方式:栈帧过多,栈帧过大。

我们可以使用参数 -Xss 去调整JVM栈的大小。

栈帧过多导致栈内存溢出:(通过递归调用)

java空闲线程堆栈 jvm 线程栈_JVM_07

   

栈帧过大导致栈内存溢出:

不方便模拟,因为栈帧中存放局部变量,操作数栈等信息所需的开销相比栈空间来说显得级小,所以不便于进行模拟。

 

扩展知识  (来自:)

栈帧中的局部变量表和操作数栈

java空闲线程堆栈 jvm 线程栈_JVM_08

(1)局部变量表
      局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量名和变量值。如果保存的数据是对象,则存放变量名和引用值。(下图未画出保存对象类型)

java空闲线程堆栈 jvm 线程栈_JVM_09

 (2)操作数栈
      Java虚拟机的解释执行引擎被称为"基于栈的执行引擎",其中所指的栈就是指-操作数栈。
      操作数栈也常被称为操作栈。

虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。比如,iadd指令就要从操作数栈中弹出两个整数,执行加法运算,其结果又压回到操作数栈中,看看下面的示例,它演示了虚拟机是如何把两个int类型的局部变量相加,再把结果保存到第三个局部变量的:
  

Java代码  

begin  
iload_0    // push the int in local variable 0 onto the stack  
iload_1    // push the int in local variable 1 onto the stack  
iadd       // pop two ints, add them, push result  
istore_2   // pop int, store into local variable 2  
end

        在这个字节码序列里,前两个指令iload_0和iload_1将存储在局部变量中索引为0和1的整数压入操作数栈中,其后iadd指令从操作数栈中弹出那两个整数相加,再将结果压入操作数栈。第四条指令istore_2则从操作数栈中弹出结果,并把它存储到局部变量区索引为2的位置。下图详细表述了这个过程中局部变量和操作数栈的状态变化,图中没有使用的局部变量区和操作数栈区域以空白表示。
  

java空闲线程堆栈 jvm 线程栈_局部变量_10