运行时栈帧结构与Tomcat类加载架构

运行时栈帧结构

栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机里面入栈到出栈的过程。

每一个栈帧都包括局部变量表、操作数栈、动态连接、方法返回和一些额外的附加信息。对于执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧,与这个栈帧相关联的方法称为当前方法。典型的栈帧结构如图:

在这里插入图片描述

  • 局部变量表:局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java编译为class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的局部变量表的最大容量。局部变量表的容量以变量槽(Variable Slot)为最小单位,一个slot可以存放一个32位以内的数据类型,Java中占用32以内的数据类型有boolean、byte、char、short、int、float、reference和returnAddress8种类型。
  • 操作数栈:操作数栈也称为操作栈,同局部变量表一样,操作数栈的最大深度也在编译的时候写入到Code属性的max_stacks数据项中。操作数栈的每一个元素都可以是任意的Java数据类型,包括long和double。32数据类型所占的栈容量为1,64位数据类型所占的栈容量为2。在方法执行的任何时候,操作数栈的深度都不会超过在max_stacks数据项中设定的最大值。

优化:让下面的栈帧的部分操作数栈和上面栈帧的部分局部变量表重叠在一起,这样在进行方法调用时就可以共用一部分数据,无需进行额外的参数复制传递。

在这里插入图片描述

  • 动态连接:每一个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。字节码中的方法调用指令以常量池中指向方法的符号引用作为参数,这些符号引用一部分会在类加载阶段或者第一次使用的时候转化为直接引用,这种转化称为静态解析。另外一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。
  • 方法返回地址:执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递到上层的方法调用者,这种退出方法的方式称为正常完成出口。在执行过程中遇到异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码使用athrow字节码指令产生的异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式称为异常完成出口,一个方法使用异常完成出口的方式退出,是不会给它的上层调用者产生任何返回值的。
  • 附加信息:虚拟机规范允许具体的虚拟机实现增加一些规范里没有描述的信息到栈帧之中,例如与调试相关的信息,这部分信息完全取决于具体的虚拟机实现,在实际开发中,一般会把动态连接、方法返回地址与其他附加信息全部归为一类,称为栈帧信息。
方法调用

方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。

解析:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期间是不可变的。换句话说,调用目标在程序执行代码写好、编译器进行编译时就必须确定下来,这类方法的调用称为解析。在Java语言中符合“编译器可知,运行期不可变”这个要求的方法,主要包括静态方法和私有方法。

5条方法调用字节码指令:

inokestatic:调用静态方法

invokespecial:调用实例构造器方法、私有方法和父类方法。

invokevirtual:调用所有的虚方法。

invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象

invokedynamic:先在运行时动态解析出此调用点限定符所引用的方法,然后在执行该方法。

只要被invokestatic和invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本,符合这个条件的有静态方法、私有方法、实例构造器、父类方法4类,他们在类加载的时候就会把符号引用解析为该方法的直接引用。这些方法可以称为非虚方法,与之相反,其他称为虚方法。

分派

静态分派:所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。典型应用是重载。发生在编译阶段。

动态分派:在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。

单分派与多分派:方法的接受者与方法的参数统称为方法的宗量。根据分派基于多少种宗量,可以将分派划分为单分派和多分派两种。单分派是根据一个宗量对目标方法进行选择,多分派则是根据多于一个宗量对目标方法进行选择。Java语言是一门静态多分派,动态单分派的语言。

Tomcat类加载器架构

在这里插入图片描述

灰色背景的三个类加载器是JDK默认提供的类加载器,而CommonClassLoader、CatalinaClassLoader、SharedCLassLoader和WebappClassLoader则是Tomcat自己定义的类加载器,他们分别加载/common/、/server/、/shared/*、和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。

CommonClassLoader能加载的类都可以被CatalinaClassLoader和SharedCLassLoader使用,而CatalinaClassLoader和SharedCLassLoader自己能加载的类则与对方相互隔离。WebappClassLoader可以使用SharedCLassLoader加载到的类,但各个WebappClassLoader实例之间相互隔离。而JasperLoader的加载范围仅仅只是这个JSP文件所编译出来的那一个class,他出现的目的就是为了被丢弃:当服务器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现Jsp文件的HotSwap功能。

猜你喜欢

转载自blog.csdn.net/gkq_tt/article/details/86560559