一、前言

(栈并不是越大越好,越多可以防止出现StackOverflowError晚点出现,但是栈越大,也就代表着虚拟机栈是一定的,你的栈越大,别的栈就会小)

二、 什么是 Java virtual machine?

  1. 栈的概述
    每创建一个线程就会创建一个Java栈,每一个Java栈中都会有很多栈帧(局部变量表 | 操作数栈 | 动态链接 | 方法返回地址 | 一些附加信息) 掌握
    解释:
  • (1). 虚拟机栈(Java Virtual Machine Stacks)和线程是紧密联系的,每创建一个线程时就会对应创建一个Java栈,所以Java栈也是"线程私有"的内存区域,这个栈中又会对应包含多个栈帧,每调用一个方法时就会往栈中创建并压入一个栈帧,栈帧是用来存储方法数据和部分过程结果的数据结构,每一个方法从调用到最终返回结果的过程,就对应一个栈帧从入栈到出栈的过程
    [先进后出]
  • (2). 栈帧中有如下部分组成:
  • (3). 栈的其他介绍
  1. 存放于栈中的东西如下 [掌握]

(8种基本类型的变量+对象的引用变量+实例方法都是在函数的栈内存中分配[局部变量])

  1. 栈内存溢出(StackOverflowError) -Xss参数
  • 栈帧过多导致栈内存溢出(方法的递归调用,没设置正确停止条件)
  • 栈帧过大(栈帧大小>栈内存)
Exception in thread "main" java.lang.StackOverflowError
//sayHello()发生了递归
public class DemoT {public static void main(String[] args) {sayHello();}public static void sayHello(){sayHello();}
}

二、 局部变量表(LocalVariables) 掌握

  1. 定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量(这些数据类型包括各种基本数据类型、对象引用(reference)以及return Address类型)

  2. 由于局部变量是建立在线程的栈上,是线程私有数据,因此不存在数据安全问题

  3. 局部变量表所需容量大小是在编译期确定下来的。(并保存在方法Code属性的maximum local variables数据项中,在方法运行期间不会改变局部变量表的大小的)

//使用javap -v 类.class 或者使用jclasslib
public class LocalVariableTest {public static void main(String[] args) {LocalVariableTest test=new LocalVariableTest();int num=10;test.test1();}public static void test1(){Date date=new Date();String name="xiaozhi";}
}

jclasslib说明:


  1. 关于slot的理解(引用数据类型(方法的返回地址)占用1个slot)
  • 局部变量表,是基本的存储单元是slot(变量槽)
  • 在局部变量表中,32位以内的类型只占有一个slot(包括引用数据类型),64位的类型(long和double)占有两个slotbyte、short、char在存储前被转换为int,boolean也被转换为int(0表示fasle,非0表示true) long和double则占据两个slot
  1. Jvm会为局部变量表中的每一个slot都分配一个访问索引,通过这个索引即可成功访问到局部变量表中指定的局部变量值

  2. 如果需要访问局部变量表中一个64bit的局部变量值时,只需要使用前一个索引即可(比如:访问long或double类型变量)

  3. 如果当前帧是由构造方法或者实例方法创建,那么该对象引用this将会放在index为0的slot处

三、操作数栈(operand stack) (掌握)

  1. 每一个独立的栈帧中除了包含局部变量表以外,还包含了一个后进先出的操作数栈,也可以称之为表达式栈

  2. 操作数栈,在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈或出栈

  3. 如果被调用的方法带有返回值的话,其返回值将会被压入当前栈帧的操作数栈中

  4. 操作数栈,主要用于保存计算机过程的中间结果,同时作为计算过程中变量临时的存储空间

  5. 操作数栈的具体说明:

解释:




四 、动态链接(Dynamic Linking)

  1. 运行时常量池位于方法区,字节码中的常量池结构如下:

  2. 为什么需要常量池呢?
    (常量池的作用,就是为了提供一些符号和常量,便于指令的识别。下面提供一张测试类的运行时字节码文件格式)

  3. 每一个栈帧内部都包含一个指向运行时常量池Constant pool或该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接。比如invokedynamic指令

  4. 在Java源文件被编译成字节码文件中时,所有的变量和方法引用都作为符号引用(symbolic Refenrence)保存在class字节码文件(javap反编译查看)的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用(#)最终转换为调用方法的直接引用。

五、 方法的调用:(小插曲)难点

  1. 静态链接(早期绑定):当一个 字节码文件被装载进JVM内部时,如果被调用的目标方法在编译期可知,且运行期保持不变时。这种情况下将调用方法的符号引用转换为直接引用的过程称之为静态链接
    (invokestatic | invokespecial)

  2. 动态链接(晚期绑定):如果被调用的方法在编译期无法被确定下来,也就是说,只能够在程序运行期将调用方法的符号引用转换为直接引用,由于这种引用转换过程具备动态性,因此也就被称之为动态链接。体现了多态
    (invokevirtual | invokeinterface)

  3. 非虚方法: 如果方法在编译器就确定了具体的调用版本,这个版本在运行时是不可变的。这样的方法称为非虚方法
    (静态方法、私有方法、final方法、实例构造器(实例已经确定,this()表示本类的构造器)、父类方法(super调用)都是非虚方法)

  4. 其他所有体现多态特性的方法称为虚方法

  5. 如下指令要重点掌握

普通调用指令:
1.invokestatic:调用静态方法,解析阶段确定唯一方法版本;
2.invokespecial:调用方法、私有及父类方法,解析阶段确定唯一方法版本;
3.invokevirtual:调用所有虚方法;
4.invokeinterface:调用接口方法; 动态调用指令(Java7新增):
5.invokedynamic:动态解析出需要调用的方法,然后执行 . 前四条指令固化在虚拟机内部,方法的调用执行不可人为干预,而invokedynamic指令则支持由 用户确定方法版本。
6.其中invokestatic指令和invokespecial指令调用的方法称为非虚方法
7.其中invokevirtual(final修饰的除外,JVM会把final方法调用也归为invokevirtual指 令,但要注意final方法调用不是虚方法)、invokeinterface指令调用的方法称称为虚方法。

/*** 解析调用中非虚方法、虚方法的测试*/
class Father {public Father(){System.out.println("Father默认构造器");}public static void showStatic(String s){System.out.println("Father show static"+s);}public final void showFinal(){System.out.println("Father show final");}public void showCommon(){System.out.println("Father show common");}}public class Son extends Father{public Son(){super();}public Son(int age){this();}public static void main(String[] args) {Son son = new Son();son.show();}//不是重写的父类方法,因为静态方法不能被重写public static void showStatic(String s){System.out.println("Son show static"+s);}private void showPrivate(String s){System.out.println("Son show private"+s);}public void show(){//invokestaticshowStatic(" 大头儿子");//invokestaticsuper.showStatic(" 大头儿子");//invokespecialshowPrivate(" hello!");//invokespecialsuper.showCommon();//invokevirtual 因为此方法声明有final 不能被子类重写,所以也认为该方法是非虚方法showFinal();//虚方法如下//invokevirtualshowCommon();//没有显式加super,被认为是虚方法,因为子类可能重写showCommoninfo();MethodInterface in = null;//invokeinterface  不确定接口实现类是哪一个 需要重写in.methodA();}public void info(){}}interface MethodInterface {void methodA();
}

六、 关于invokedynamic指令

  1. JVM字节码指令集一直比较稳定,一直到java7才增加了一个invokedynamic指令,这是Java为了实现【动态类型语言】支持而做的一种改进

  2. 动态类型语言和静态类型语言两者的却别就在于对类型的检查是在编译期还是在运行期,满足前者就是静态类型语言,反之则是动态类型语言。

  3. Java是静态类型语言(尽管lambda表达式为其增加了动态特性),js,python是动态类型语言

Java:String info = "小智";//静态语言JS:var name = "小智“;var name = 10;//动态语言Pythom: info = 130;//更加彻底的动态语言

七、方法返回地址(Return Address)

理解如下话:

(pc寄存器每执行一条指令都会被改变 而返回地址在调用call之前一直是上一条call后面的地址,不改变)

  1. 存放调用该方法的PC寄存器的值
  2. 执行引擎遇到任意一个方法返回的字节码指令(return),会有返回值传递给上层的方法调用者,简称正常完成出口;
- 1.一个方法在正常调用完成之后究竟需要使用哪一个返回指令还需要根据方法返回值的实际 数据类型而定
- 2.在字节码指令中,返回指令包含ireturn(当返回值是boolena、byte、char、short和int 类型时使用)、lreturn、freturn、dreturn以及areturn(引用类型的)
- 3.另外还有一个return指令供声明为void的方法、实例初始化方法、类和接口的初始化方法使用

  1. 在方法执行的过程中遇到了异常(Exception),并且这个异常没有在方法内进行处理,也就是只要在本方法的异常表中没有搜素到匹配的异常处理器,就会导致方法退出,简称异常完成出口

JVM_03 运行时数据区 [ 虚拟机栈 ]相关推荐

  1. JVM_03 运行时数据区 [ 方法区]

    一.方法区的概述 方法区在JVM启动的时候被创建,并且它的实际的物理内存空间和Java堆区一样都可以是不连续的 | 关闭Jvm就会释放这个区域的内存 方法区时逻辑上是堆的一个组成部分,但是在不同虚拟机 ...

  2. JVM_03 运行时数据区 [ 程序计数器+本地方法栈 ]

    一. 程序计数器 ①. 作用,是用来存储指向下一条指令的地址,也即将要执行的指令代码.由执行引擎读取下一条指令 ②. 特点:①. 是线程私有的 ②. 不会存在内存溢出 ③. 注意:在物理上实现程序计数 ...

  3. JVM_03 运行时数据区[ 堆 ]

    一.堆的概述 堆的概述(共享|垃圾回收) ①. 一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域 ②. Java堆区在JVM启动的时候即被创建,其空间大小也是确定的.是Jvm管理最大的 ...

  4. jvm:运行时数据区--操作数栈

  5. JVM运行时数据区分析

    #1.概述 整个JVM构成⾥⾯,由三部分组成:类加载器机制.运⾏时数据区.执⾏引擎. #2.JVM运行时数据区的规范 我们来聊聊这个规范怎么理解,目前运行数据区共分为了方法区.堆.虚拟机栈.本地方法栈 ...

  6. JVM学习(七):运行时数据区(精讲)

    目录 一.运行时数据区概述 1.1 概述 1.2 运行时数据区中的GC和OOM 二.程序计数器(PC寄存器,Program Counter Register) 2.1 介绍 2.2 作用 2.3 特点 ...

  7. Java8 JVM运行时数据区概述 (极其详细长文)

    文章目录 运行时数据区概述 JVM中的线程说明 PC寄存器(PC Register) PC寄存器介绍 使用举例 问题:使用PC寄存器存储字节码指令地址有什么用?为什么使用PC寄存器存储? 问题:为什么 ...

  8. Java程序员必知必会之JVM运行时数据区

    JVM类加载概述 JVM垃圾回收概述 运行时数据区概述 程序计数器(Program Counter Register) 是一块较小的内存空间,可以看作是当前线程所执行字节码的行号指示器,指向下一个将要 ...

  9. JVM系列之运行时数据区

    1.运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是 ...

最新文章

  1. 自动装箱与自动拆箱的一些问题
  2. 基于matlab的卷积码实验报告,基于MATLAB的卷积码编译码设计仿真.doc
  3. ES9的新特性:异步遍历Async iteration
  4. 936焊台(恒温电烙铁)温度不可调的维修 (Z)
  5. Kali渗透测试技术实战
  6. js的栈堆与浅拷贝、深拷贝的理解
  7. 字符串操作之字符串连接
  8. mysql数据库标识符大小写_SQL语法区分大小写吗?
  9. 计算机辅助几何设计等值曲线,中国科学技术大学硕士专业:计算机辅助几何设计...
  10. VS2015 C#程序打包成.exe之installshield使用教程
  11. 子查询定义从句总结(WITH AS 语句)
  12. def是c语言特殊标识符吗,typedef和def究竟有什么区别?
  13. 在线分析仪器(四)在线气体分析成套系统简述
  14. 计算机统计大数据库,统计数据库
  15. mysql 关闭防火墙 命令_linux常用命令和关闭防火墙
  16. MDT 2013 Update 1 Preview 部署 Windows 10之WDS部署服务配置
  17. mysql对韵母分组,基于MySQL的中文发音查询的元级实现
  18. 融云 php sdk下载,Android 即时语音聊天工具 开发
  19. 小米max3支持html吗,小米Max3这款手机是否有OTG功能,支持OTG鼠标和U盘吗?
  20. 安装keil5出现you are not logged in as an 'Administrator'

热门文章

  1. VS Supercharger插件的破解
  2. 频繁项集挖掘之Aprior和FPGrowth算法
  3. py---------面向对象进阶
  4. python跟踪脚本进度(类似bash-x)
  5. 条款五:对应的new和delete要采用相同的形式
  6. EMVTag系列9《卡片管理数据》
  7. The Art of Unix Programming
  8. YOLO v3解析与实现
  9. 时频分析:短时傅里叶变换实现(2)
  10. 英语写作中常见语法总结(二)