1.数据结构栈
栈是一种比较简单的数据结构,后进先出。栈本身是一个线性表,但是这个表中只有一端允许数据的进出。栈的常用操作包括入栈push和出栈pop,对应于数据的压入和弹出。由于栈后进先出的特性,常可以作为数据操作的临时容器,对数据的顺序进行调控。
2.本地方法栈
2.1 本地方法(Native Method)
本地方法是由非 Java 语言编写的,编译成和处理器相关的机器代码。它保存在动态链接库中,Windows 系统即 .dll 文件中,格式是各个平台专有的。Java 方法是与平台无关的,但是本地方法不是,运行中的 Java 方法调用本地方法时虚拟机会装载包含这个本地方法的动态库,并调用这个本地方法。
通过本地方法,Java 程序可以直接访问底层操作系统的资源,调用本地方法会使程序变得与平台相关,因为本地方法的动态库是与平台相关的,此外使用本地方法还可能把程序变得和特定的 Java 平台实现相关。而本地方法接口(Java Native Interface JNI)使得本地方法可以在特定主机系统的任何一个 Java 平台实现上运行。如果希望使用特定主机上的资源,它们又无法通过 Java API 访问,那么可以写一个平台相关的 Java 程序来调用本地方法,如果希望保证程序的平台无关性,那么只能通过 Java API 来访问底层系统资源。
标识符 native 可以与所有其它的 Java 标识符连用,但是 abstrat 除外。因为 native 表示这些方法是有实现体的,只不过这些实现体是非 Java 的,但是abstract 却显然的指明这些方法无实现体。native 与其它 Java 标识符连用时,其意义同非本地并无差别,比如 native static 表明这个方法可以在不产生类的实例时直接调用,比如用一个native method 去调用一个 C 的类库时。
2.2 本地方法栈(Native Method Stack)
本地方法栈用于管理本地方法的调用是线程私有的,并不是所有 JVM 都支持本地方法。因为 JVM 规范并没有明确要求本地方法栈的使用语言、具体实现方式、数据结构等。如果 JVM 产品不打算支持 native 方法,也可以无需实现本地方法栈。
-
本地方法是使用C语言实现的
-
它的具体做法是
Native Method Stack
中登记native方法,在Execution Engine
执行时加载本地方法库。线程调用本地方法就不再受虚拟机限制了,它和虚拟机拥有同样的权限。 -
本地方法可以通过 JNI 来访问虚拟机内部的运行时数据区,它甚至可以直接使用本地处理器中的寄存器,直接从本地内存的堆中分配任意数量的内存。
-
在 Hotspot JVM 中,直接将本地方栈和虚拟机栈合二为一。
本地方法栈与虚拟机栈的作用是相似的,都会抛出OutOfMemoryError和StackOverFlowError,都是线程私有的,主要的区别在于:
- 虚拟机栈执行的是 Java 方法
- 本地方法栈执行的是 native 方法
3.虚拟机栈
虚拟机栈(Java Virtual Machine Stacks)。每个线程在创建的时候都会创建一个虚拟机栈,其内部保存的栈帧(Stack Frame),对应着 Java 方法调用,是线程私有的,生命周期和线程一致。主管 Java 程序的运行,它保存方法的局部变量、部分结果,并参与方法的调用和返回。特点:
- 是一种快速有效的分配存储方式,访问速度仅次于程序计数器。
- JVM 直接对虚拟机栈的操作只有两个:每个方法执行,伴随着入栈(进栈/压栈),方法执行结束出栈。
- 栈不存在垃圾回收问题。
栈中可能出现的异常:
Java 虚拟机规范允许 Java虚拟机栈的大小是动态的或者是固定不变的:
- 如果采用固定大小的 Java 虚拟机栈,那每个线程的 Java 虚拟机栈容量可以在线程创建的时候独立选定。如果线程请求分配的栈容量超过 Java 虚拟机栈允许的最大容量,Java 虚拟机将会抛出一个 StackOverflowError 异常
- 如果 Java 虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那 Java 虚拟机将会抛出一个OutOfMemoryError异常
可以通过参数-Xss
来设置线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度。
每个**栈帧(Stack Frame)**中存储着:
- 局部变量表(Local Variables)
- 操作数栈(Operand Stack)(或称为表达式栈)
- 动态链接(Dynamic Link