Java虚拟机栈详解
前言
虚拟机栈也称为Java栈,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)
栈特点基本介绍
- Java虚拟机栈属于线程私有,它的生命周期与线程相同(随线程而生,随线程而灭)
- 虚拟机栈说明了线程运行时的瞬时状态
- 每次方法调用,都会产生对应的栈帧
- 栈帧包括局部变量表、操作数栈、动态链接、方法返回地址和一些附加信息
- 每个方法被调用至执行完毕的过程,就对应这个栈帧在虚拟机栈中从入栈到出栈的完整过程
- 栈的深度有限制
局部变量表
- 局部变量表(Local Variable Table)是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量,并在Java编译为Class文件时,就已确定该方法所需分配的局部变量表的最大容量
- 局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)「String是引用类型」,对象引用(reference类型) 和 returnAddress类型(它指向了一条字节码指令的地址)
- 线程私有,不允许跨线程访问,随方法调用创建,方法退出销毁
- 编译期间长度已经确定,局部变量元数据存储在字节码中
- 局部变量表是栈帧最主要的存储空间,决定了栈的深度
操作数栈
保存中间计算的临时结果,字节码指令在执行过程中的中间计算结果存储在操作数栈
- 操作数栈(Operand Stack)也常被成为操作栈,是一个后入先出栈,用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间。其最大深度在编译时就被写到了Code属性的max_stacks中
- 操作数栈在方法的执行过程中,根据字节码指令往栈中写入数据或提取数据,即入栈和出栈操作。虽然栈是用数组实现的,但根据栈的特性,对栈中数据访问不能通过索引,而是只能通过标准的入栈和出栈操作来完成一次数据访问
动态连接
将符号引用转为直接引用
- 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用
- 持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)
- 在类加载阶段中的解析阶段会将符号引用转为直接引用,这种转化也称为静态解析
- 另外的一部分将在每一次运行时期转化为直接引用。这部分称为动态连接
方法出口
存放调用方法的程序计数器
当一个方法开始执行后,有2种方式可以退出这个方法
- 方法返回指令 : 执行引擎遇到一个方法返回的字节码指令(return),这时候可能会有返回值传递给上层的方法调用者,这种退出方式称为正常完成出口
- 异常退出 : 在方法执行过程中遇到了异常,并且没有处理这个异常,就会导致方法退出
- 一般来说,方法正常退出时,调用者的PC计数器的值可以作为返回地址,栈帧中会保存这个计数器值,而方法异常退出时,返回地址要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息
假如有下面这样一段代码,在一个main方法中调用了method1,再在method1调用method2
案例演示
public class MyMethod {public static void main(String[] args) {new MyMethod().method1();}public void method1(){System.out.println("进入method1 ...");int a=1;int b=2;method2();System.out.println("退出method1");}public void method2(){System.out.println("进入method2 ...");int c=3;int d=4;System.out.println("退出method2");}}
对应一个简单的栈帧图示可表示如下:
在数据结构中,栈属于一种典型的先进后出的数据结构,即先被压入栈的数据最后出来,对应到上面的案例中,Method2最后入栈,因此当Method2方法执行完毕后,最先从栈弹出,直到Main方法完成,当前这个线程的虚拟机栈就销毁,这也符合预期的方法调用返回结果
在idea中通过安装jclasslib插件,可以清楚的看到方法的字节码运行过程中的详细信息,如下,我们分析method2这个方法在调用时的栈帧过程时候,通过显示的字节码信息可以看出来该方法中的局部变量信息,结果返回时机等
栈空间设置
- Java1.5之后默认每个栈空间大小为1MB
- Java启动参数: -Xss 数值【k|m|g】
- 栈内存分配决定了栈的最大深度,栈内存的深度在实际中是动态的,会随着每个栈中调用方法的数量不同而不同,在某些极端情况下,比如栈的空间不够了或者打满了,就会抱出栈溢出的错误,即OutOfMemoryError异常;
栈的两种常用空间配置:
- 固定长度(推荐):达到上限时,StackOverFlowError
- 动态扩展,当可用内存不足时,OutOfMemoryError(OOM)
- 实际开发中,尽量不使用动态扩展的方式设置,否则因为某个线程的OOM导致整个服务器资源被耗尽而拖垮其他的服务或者功能(本人遇到过类似的生产问题),简而言之,动态扩展增大了资源调配的控制难度
StackOverFlowError案例演示
public class StackOverFlowError {private static long count = 0;public static void main(String[] args) {test();}public static void test(){count++;System.out.println("正在进行第:" + count + "次调用");test();}}
这是一段自我调用最后造成死循环的调用,最后一定会由于栈内存不足报栈溢出的错误,但是设置不同的栈空间大小,这个count的次数理论上会不一样
以默认的栈空间大小
调大栈内存
通过启动参数配置之后再次运行,可以承受的最大调用次数明显增大了(即栈的深度增大了)
native本地方法栈
我们知道,Java属于上层语言,在对操作系统的控制层面上相比c,c++逊色不少,但是为了实现某些功能需要调用操作系统提供的相关函数,而Java中的native方法的职责就为这一功能的实现提供了一个类似转换的接口
在native方法中,只提供了接口,没有具体的实现,实现部分由C或C++去实现,总结来说:
- 一个native方法就是一个Java调用非Java代码的接口
- 定义一个native方法时,并不提供具体的实现
- native的方法可以调用其他语言接口实现对操作系统更加底层的操作,比如windows下对dll文件的调用
Java虚拟机栈详解相关推荐
- JVM(Java虚拟机)详解(JVM 内存模型、堆、GC、直接内存、性能调优)
JVM(Java虚拟机) JVM 内存模型 结构图 jdk1.8 结构图(极简) jdk1.8 结构图(简单) JVM(Java虚拟机): 是一个抽象的计算模型. 如同一台真实的机器,它有自己的指令集 ...
- Java虚拟机组成详解
一.jvm的主要组成部分 类加载器(ClassLoader) 运行时数据区(Runtime Data Area) 执行引擎(Execution Engine) 本地库接口(Native Interfa ...
- JVM(二)Java虚拟机组成详解
导读:详细而深入的总结,是对知识"豁然开朗"之后的"刻骨铭心",想忘记都难. Java虚拟机(Java Virtual Machine)下文简称jvm,上一篇我 ...
- 【JVM】运行时数据区介绍,程序计数器和虚拟机栈详解
JVM越来越是Java面试中的重头戏,今天来总结一下JVM运行时数据区的相关内容. 文章目录 JVM运行时数据区 JVM运行时数据区内部结构 程序计数器(PC寄存器) 程序计数器的介绍 PC寄存器的实 ...
- JVM 虚拟机栈详解
当Java虚拟机运行程序时.每当一个新的线程被创建时.Java 虚拟机都会分配一个虚拟机栈,Java虚拟机栈是以帧为单位来保存线程的运行状态.Java栈只会有两种操作:以帧为单位进行压栈跟出栈. 某个 ...
- 一篇文章了解Java虚拟机,Java虚拟机内存详解
虚拟机介绍 Java虚拟机(JVM)一种用于计算机设备的规范,可用不同的方式(软件或硬件)加以实现.编译虚拟机的指令集与编译微处理器的指令集非常类似.Java虚拟机包括一套字节码指令集.一组寄存器.一 ...
- java虚拟机参数详解
Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOME\bin\java –option 来启动,-option为虚拟机参数,JAVA_H ...
- Java虚拟机结构详解,详细分析其构成
Java虚拟机 java虚拟机(java virtual machine,JVM),一种能够运行java字节码的虚拟机.作为一种编程语言的虚拟机,实际上不只是专用于Java语言,只要生成的编译文件匹配 ...
- Java内存溢出详解之Tomcat配置
Java内存溢出详解 转自:http://elf8848.iteye.com/blog/378805 一.常见的Java内存溢出有以下三种: 1. java.lang.OutOfMemoryError ...
最新文章
- 【iOS-cocos2d-X 游戏开发之十四】cocos2dx(c++)中访问object函数
- 剑指offer:和为S的两个数字
- 5、HTML块级元素及行内元素
- mysql5.7主从
- OpenGL环境多维数据集映射
- 由胡润百富榜联想到的
- 结组项目-四则运算3
- .NET开源项目介绍及资源推荐:数据持久层
- stucts2 页面上的值如何与Action的属性值对应
- 大华工业相机使用说明_大华C900系列SSD | 极速传输,不负美名
- PHP不仅仅是PHP
- Mac搭建本地服务器及测试demo
- 基础集合论 第一章 4 子集
- spoon在linux上运行,kettle在linux上运行
- openstack进阶:虚拟桌面usb重定向(usb映射)
- 腾讯天龙八部手游服务器账号上线,天龙八部手游服务器的注册已达到上限 服务器注册上限怎么解决...
- 使用 Python 制作图片和语音验证码
- 2019 年度程序员薪酬报告:40 岁以后普遍遭遇收入天花板
- Rancher学习日记4
- 哈夫曼算法以及求哈夫曼编码
热门文章
- 转: ImageMagick 命令行的图片处理工具(客户端与服务器均可用)
- 深入浅出 Java 8 Lambda 表达式
- 演示对sys用户和普通用户进行审计的示例
- ssh 免密登录并用脚本群起服务
- Docker 容器的运行(八)
- JDBC连接数据库:单线程、多线程、批处理插入数据的对比
- DEDECMS给图集图片{dede:productimagelist}自动编号
- SQL error: cannot use the special principal 'sa'
- curl只能抓取页面的部分内容的原因
- Windows Phone 8初学者开发—第6部分:设置应用程序的样式