银川网站建设有哪些,16岁做分期网站,网站做的简单是什么意思,动漫制作技术专业常识虚拟机栈
虚拟机栈概述
栈是运行时的单位#xff0c;而堆是存储的单位。 栈解决程序的运行问题#xff0c;即程序如何执行#xff0c;或者说如何处理数据。堆解决的是数据存储的问题#xff0c;即数据怎么放#xff0c;放在那儿。 虚拟机栈的基本内容 Java虚拟机栈 Java…虚拟机栈
虚拟机栈概述
栈是运行时的单位而堆是存储的单位。 栈解决程序的运行问题即程序如何执行或者说如何处理数据。堆解决的是数据存储的问题即数据怎么放放在那儿。 虚拟机栈的基本内容 Java虚拟机栈 Java虚拟机栈(Java Virtual Machine Stack)早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈其内部保存一个个的栈帧(Stack Frame)对应着一次次的Java方法调用。 是线程私有的。 生命周期 生命周期和线程是一致。 作用 主管Java程序的运行它保存方法的局部变量部分结果并参与方法的调用和返回。 栈的特点(优点) 栈是一种快速有效的分配存储方式访问速度仅次于程序计数器。 JVM直接对Java栈的操作只有两个 每个方法执行伴随着进栈(入栈压栈) 执行结束后的出栈工作 对于栈来说不存在垃圾回收问题。 设置栈内存大小 使用参数-Xss选项来设置线程的最大栈空间栈的大小直接决定了函数调用的最大可达深度。 面试题 Java虚拟机规范允许Java栈的大小是动态的或者是固定不变的。 如果采用固定大小的Java虚拟机栈那每一个线程的Java虚拟机栈容量可以那种线程创建的时候独立选定。如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量Java虚拟机将会抛出一个StackOverflowError异常。 如果Java虚拟机栈乐可以动态并且在尝试扩展的时候无法申请到足够的内存去创建对于的虚拟机栈那Java虚拟机将会抛出一个OutofMemoryError异常。
栈的存储单位 栈中存储什么 每个线程都有自己的栈栈中的数据都是以栈帧(Stack Frame)的格式存在。 在这个线程上正在执行的每个方法都各自对于一个栈帧(Stack Frame)。 栈帧是一个内存区块是一个数据集维系着方法执行过程中的各自数据信息。 栈运行原理 JVM直接对Java栈的操作只有两个就是对栈帧的压栈和出栈遵循着先进后出/后进先出原则。 在一条活动线程中一个时间点上只会有一个活动的栈帧。即只有当前正在执行的方法的栈帧栈顶栈帧是有效的这个栈帧被称为当前栈帧Current Frame,与当前栈帧相对应的方法就是当前方法Current Method定义这个方法的就是当前类Current Class. 执行引擎运行的所有字节码指令只针对当前栈帧进行操作。 如果在该方法中调用了其它方法对应的新的栈帧会被创建出来放在栈的顶端成为新的当前栈。 不同线程中所包含的栈帧是不允许存在相互引用的即不可能在一个栈帧之中引用另外一个线程的栈帧。 如果当前方法调用了其它方法方法返回之际当前帧 会传回此方法的执行结果当前一个栈帧接着虚拟机会丢弃当前栈帧使得前一个栈帧重新成为当前栈帧。 Java方法中有两种返回函数的方式无论哪种都会导致栈帧被弹出 正常的函数返回 return 抛出异常 栈帧的内部结构 局部变量表Local Variables 操作数栈Operand Stack或表达栈 动态链接Dynamic Linking(或指向运行时常量池的方法引用) 方法返回地址Return Address(或方法正常退出或者异常退出的定义) 一些附件信息 局部变量表 局部变量表也被称为局部变量数组或本地变量表。 定义为一个数字数组主要用于存储方法参数和定义在方法体内的局部变量。 局部变量表是建立在线程的栈上是线程的私有数据不存在数据安全问题。 局部变量表所需的容量大小是在编译期确定下来的 并保存在方法的Code属性的maximum local variables数据项中。在方法运行期间是不会改变局部变量表的大小的。 方法嵌套调用的次数由栈的大小决定。一般来说栈越大方法嵌套调用次数越多。对一个函数而言它的参数和局部变量越多使得局部变量表膨胀它的栈帧就越大以满足方法调用所需传递的信息增大的需求。进而函数调用就会占用更多的栈空间导致其嵌套调用次数就会减少。 局部变量表中的变量只在当前方法调用中有效。在方法执行时虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程。当方法调用结束后随着方法栈帧的销毁局部变量表也会随之销毁。
Slot 参数值的存放总是在局部变量数组的index0开始到数组长度-1的索引结束。 局部变量表最基本的存储单元是Slot变量槽 局部变量表中存放编译期可知的各种基本数据类型(八种)引用类型在referencereturnAddress类型的变量。 在局部变量表里32位以内的类型只占用 一个slot包括returnAddress类型64位的类型long和double占用两个slot。 byteshortcharboolean在存储前被转换为int。 JVM会为局部变量表中的每一个Slot都分配一个访问索引通过这个索引即可成功访问到局部变量表中指定的局部变量值。 当一个实例方法被调用的时候它的方法参数和方法体内部定义的局部变量将会按照顺序被复制到局部变量表中的每一个Slot上 如果需要访问局部变量表中一个64bit的局部变量值时只需要使用前一个索引即可。比如访问long或double类型变量 如果当前帧是由构造方法或者实例方法 创建的那么该对象引用this将会存放在index为0的slot处其余的参数按照参数表顺序继续排列。 槽位是可以重复利用的。
操作数栈 每一个独立的栈帧中除了包含局部变量表以外还包含一个先进后出的操作数栈也可以称之为表达式栈Expression Stack 操作数栈在方法执行过程中根据字节码指令往栈中写入数据或提取数据即入栈push/出栈pop。 某些字节码指令将值压入操作数栈其余的字节码指令 将操作数取出栈。使用他们后再把结果压入栈。 比如执行复制交换求和等操作。 操作数栈主要用于保存计算过程的中间结果同时作为计算过程中变量临时的存储空间。 操作数栈就是JVM执行引擎的一个工作区当一个方法刚开始执行的时候一个新的栈帧也会随之被创建出来这个方法的操作数栈是空的。 每一个操作数栈都会拥有一个明确的栈深度用于存储数值其所需的最大深度在编译期就定义好了保存在方法的Code属性中为max_stack的值。 栈中的任何一个元素都是可以任意的Java数据类型。 32bit的类型占用一个栈单位深度。 64bit的类型占用两个栈单位深度。 操作数栈并非采用访问索引的方式来进行数据访问的而是只能通过标准的入栈(push)和出栈(pop)操作来完成一次数据访问。 如果被调用的方法带有返回值的话其返回值将会被压入当前栈帧的操作数栈中并更新PC寄存器中下一条需要执行的字节码指令。 操作数栈中元素的数据类型必须为字节码指令的序列严格匹配这由编译器在编译器期间进行验证同时在类加载过程中的类检验阶段的数据流分析阶段再次验证。 这里说的Java虚拟机的解释引擎是基于栈的执行引擎其中的栈指的就是操作数栈。
代码追踪 反编译指令javap -v 类名.class 栈顶缓存技术 将栈顶元素全部缓存在物理CPU的寄存器中以此降低对内存的读/写次数提示执行引擎的执行效率。 动态链接 指向运行时常量池的方法引用 常量池 为了提供一些符合和常量便于指令的识别。 每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接Dynamic Linking。比如invokedynamic指令。 在Java源文件被编译到字节码文件中时所有的变量和方法引用都作为符合引用Symbolic Reference保存在class文件的常量池里。比如描述一个方法调用了另外的其它方法时就是通过常量池中指向方法的符合引用来表示的那么动态链接的作用就是为了将这些符合引用转换为调用方法的直接引用。 方法的调用解析与分派
方法的调用 在JVM中将符号引用转换为调用方法的直接引用与方法的绑定机制相关。 静态链接 当一个字节码文件被装载进JVM内部时如果被调用的目标方法在编译期可知且运行期保持不变时。这种情况下将调用方法的符号引用转换为直接引用的过程称之为静态链接。 动态链接 如果被调用的方法在编译期无法被确定下来也就是说只能够在程序运行期将调用方法的符号引用转换为直接引用由于这种引用转换过程具备动态性因此也就被称之为动态链接。
对应的方法的绑定机制为早期绑定Early Binding和晚期绑定Late Binding。绑定是一个字段方法或者类在符合引用被替换为直接引用的过程仅仅发送一次。 早期绑定 早期绑定就是指被调用的目标方法如果在编译期可知且运行期保持不变时即可将这个方法与所属的类型进行绑定这样一来由于明确了被调用的目标方法究竟是那一个因此 也就可以使用静态链接的方式将符号引用转换为直接引用。 晚期绑定 如果被调用的方法在编译期无法被确定下来只能够 在 程序运行期根据 实际的类型绑定相关的方法这种绑定方式也就称之为晚期绑定。
虚方法与非虚方法
非虚方法 如果在编译期就确定了具体的调用版本这个版本在运行时是不可变的。这样的方法称为非虚方法。 静态方法私有方法final方法实例 构造器父类方法都是虚方法
虚方法 不是非虚方法的方法 虚拟机提供的方法调用指令 普通调用指令 invokestatic调用静态方法解析阶段确定唯一方法版本 invokespecial调用init方法私有方法及父类方法解析阶段确定唯一方法版本 invokevirtual调用所有虚方法 invokeinterface调用接口方法 动态调用指令 invokedynamic动态解析出所需调用的方法然后执行。 前四条指令固化在虚拟机内部方法的调用执行不可人为干预而invokedynamic指令则由用户确定版本。其中invokestatic指令和invokespecial指令调用的方法称为非虚方法其余的final修饰的除外称为虚方法。 关于invokedynamic指令
动态类型语言和静态类型语言两者的区别就在与对类型的检查是在编译期还是在运行期满足前者就是静态类型语言反之是动态类型语言。 静态类型语言是判断变量自身的类型信息动态类型语言是判断变量值的类型信息变量没有类型信息变量值才有类型信息这是动态语言的一个重要特征。 方法重写的本质 找到操作数栈顶的第一个元素所执行的对象的实际类型记住C。 如果在类型C中找到与常量 中的描述符合简单名称都相符的方法则进行权限校验如果通过则返回这个方法的直接引用查找过程结束如果不通过则返回java.lang.IllegalAccessError异常。 否则按照继承关系从下往上依次对C的各个父类进行第二步的搜索和验证过程。 如果始终没有找到合适的方法则抛出java.lang.AbstractMethodError异常。
[IllegalAccessError异常介绍] 程序视图修改一个属性或调用一个方法这个或许或方法没有权限访问。
虚方法表 在面向对象的编程中会很频繁的使用到动态分派如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适的目标的话就可能影响到执行效率。因此为了提高性能JVM采用在类的方法区建立一个虚方法表virtual method table非虚方法不会出现在表中来实现。使用索引表来代替。 每个类中都有一个虚方法表表中存放着各个方法的实际入口。 虚方法表会在类加载的链接阶段被创建并开始初始化类的变量初始值准备完成之后JVM会把该类的方法表也初始化完毕。
方法返回地址 存放调用该方法的PC寄存器的值 一个方法的结束有两种方式 正常执行完成 出现未处理的异常非正常退出 无论通过哪种方式退出在方法退出后都返回到该方法被调用的位置。方法正茬退出时调用者的pc计数器的值作为返回地址即调用该方法的指令的下一条指令的地址。而通过异常退出的返回地址是要通过异常表来确定的栈帧中一般不会保存这部分信息。
栈的相关面试题 举例栈溢出的情况StackOverflowError 通过-Xss设置栈的大小OOM 调整栈大小就能保证不出现溢出吗不能 分配的栈内存越大越好吗不是 垃圾回收是否会涉及到虚拟机栈不会的 方法中定义的局部变量是否线程安全具体问题具体分析