Java类加载机制

虚拟机类加载机制

1. 类加载的时机
1.1 类从被加载到虚拟机内存开始,到卸载出内存为止,他的整个生命周期包括:

加载
验证
准备
解析
初始化
使用
卸载

七个阶段,其中验证、准备、解析三个部分统称为链接。
1.2 加载的时机:

  • 使用new关键字实例化对象的时候、
  • 读取或设置一个类的静态字段的时候、以及调用一个类的静态方法的时候。
  • 使用reflect包中的方法进行反射调用的时候,若类没有进行过初始化,则需要先触发其初始化
    初始化一个类,发现他的父类还没有被初始化时,要先触发其父类的初始化
  • 虚拟机启动时,需要指定一个main类作为入口,虚拟机会优先初始化这个类
  • 使用动态语言支持时

1.3 以上五个场景都是属于主动引用,除此之外所有引用类的方式都不会触发初始化,称为被动引用。
1.4 当一个类在初始化时,要求其父类全部都已经初始化过了,但是一个接口在初始化时,并不会要求其父类接口全部初始化,只有在真正使用到父类接口的时候才会初始化。

2.类加载的过程
2.1 加载:加载是类加载过程的一个阶段,在这一阶段需要完成以下三件事:

  • 通过一个类的全限定名来获取定义此类的二进制字节流
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  • 在内存中生成一个代表这个类的Class对象没座位方法区这个类的各种数据的访问入口

加载阶段和链接阶段的一部分内容是交叉运行的,这些加载阶段之中执行的动作,仍然属于链接阶段的内容,这两个阶段的开始时间仍然保持着固定的先后顺序。

2.2 验证:验证是链接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,且不会危害虚拟机自身的安全,因此验证是虚拟机对自身保护的一项重要工作。

从整体上看,验证阶段有以下四个阶段:

  • 文件格式验证:验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机理解
  • 元数据验证:对字节码描述的信息进行语义分析,以保证其描述信息符合Java语言规范的要求。
  • 字节码验证:通过对数据流和控制流的分析,确定程序语义是合法的、符合逻辑的
  • 符号引用验证:对类自身以外的信息进行匹配性教研,确保解析动作能正常执行

2.3准备:准备阶段是正式为类变量分配内存并设置类变量初始值阶段。这些变量所使用的内存都在方法区中分配。这个时候设的初值依据不同机器而言,都是零值,在初始化阶段之后才会赋予自己定义的初值。

2.4 解析:虚拟机将常量池中的引用替换为直接引用。所谓符号引用,是指用一组符号来描绘所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符。

2.5 初始化:这是类加载过程的最后一步。前面的加载过程都是虚拟机主导和控制的,到了初始化阶段才真正开始执行类中定义的程序代码(字节码)。 在准备阶段已经有过一次赋值了,那个初始值是根据不同机器来取的零值。而在初始化阶段,相当于是执行了类的构造器的过程。

3. 类加载器

3.1 通过一个类的全限定名来获取描述此类的二进制字节流这一动作是放在虚拟机外部去实现的,实现这一动作的代码模块称为“类加载器”
3.2 对于任意一个类,都需要由加载他的类加载器和这个类本身一同确立其在虚拟机中的唯一性,每一个类加载器都拥有一个独立的类名称空间。
3.3 从Java虚拟机的角度来讲,只存在两种不同的类加载器:

  • 启动类加载器:由C++实现,是虚拟机的一部分
    所有其他类加载器:
  • 由Java实现,独立于虚拟机外部,全都继承自ClassLoader类。

3.4 从Java开发人员的角度来看,类加载器分为:

  • 启动类加载器 (Bootstrap ClassLoader)
  • 扩展类加载器(Extension ClassLoader)
  • 应用程序类加载器(AppClassLoader)

3.5 我们的应用程序都是由这三种类加载器互相配合进行加载的,她们之间的关系采用了双亲委派模型,这一模型的工作过程是:如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是那这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的类加载请求最终都会传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成工作时,子加载器才会尝试自己去加载。

虚拟机字节码执行引擎

首先,所有Java虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果。

运行时栈帧

  1. 栈帧:用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态链接和方法返回地址等信息。
  2. 每一个栈帧都包括了局部变量表、操作数栈、动态链接、方法返回地址和一些额外的附加信息,在编译的时候内存就已经分配好了,不会收到程序运行期变量数据的影响,而仅仅取决于具体的虚拟机实现。
  3. 一个线程中的方法调用链很长,所以栈里面有很多栈帧,而只有位于栈顶的那个栈帧才是当前有效的,称为当前栈帧。

栈帧的结构

1.局部变量表:一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。局部变量表的容量以Slot为最小单位,一个Slot可以存放一个32位以内的数据类型,对于64位的数据类型,虚拟机回以高位对齐的方式为其分配两个连续的Slot空间 。虚拟机通过索引定位的方式使用局部变量表,索引值的范围是从0开始到局部变量表最大的Slot数量。

2.操作数栈:也称为操作栈,在方法的执行过程中,会有各种字节码指令往操作栈中写入和提取内容。操作数栈中的元素的数据类型必须与字节码指令的序列严格匹配。在概念模型中,两个栈帧作为虚拟机栈的元素,是完全相互独立的,但在大多数虚拟机的实现中会做一些优化处理,即让两个栈帧出现一部分重叠,让下面占帧的部分操作数栈与上面栈帧的部分局部表重叠在一起,这样在进行方法调用时就可以共用一部分数据,无需进行额外的参数复制传递。

3.动态链接:一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用的作用是为了支持方法调用过程中的动态链接。class文件中的常量池中存在很多符号引用,字节码中的方法调用指令就以常量池中指向方法符号引用作为参数,这些富豪引用一部分将在每一次运行期间转化为直接引用,这种转化称为静态解析,另一部分将在每一次运行期间转化为直接引用,这部分称为动态链接。

4.方法返回地址:当一个方法执行后,只有两种方式可以退出这个方法,第一种方式是执行引擎遇到一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者,是否有返回值和返回值的类型讲根据遇到何种方法返回指令来决定,这种退出方式称为正常完成出口。

另一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体中得到处理导致方法退出,这种退出方式称为异常完成出口,一个方法使用异常完成初九的方式退出,是不会给他的上层调用者产生任何返回值。无论采用何种方式退出,都要返回到被调用的位置。

正常退出:调用者的PC计数器的值可以作为返回地址,栈帧中很可能会保存这个计数值
异常退出:返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。
5.附加信息:一些规范里没有描述的信息,例如调试相关的信息等

方法调用

方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本,一切方法调用在Class文件中存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址。

1.解析:所有方法调用中的目标方法子Class文件里都是一个常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用,这总解析用来处理静态方法和私有方法。前者与类型直接相关,后者在外部不可被访问,因此他们都不可在其他地方被继承或重写

2.分派:Java的三个特性:继承、封装、多态,其中在多态特性中的体现--重载和重写,是由分派决定的。分派由静态分派和动态分派。每一个对象在创建时都有一个静态类型和一个实际类型。静态类型和实际类型在程序运行期间都可以发生一些变化,区别在于静态类型的变化仅仅在使用时发生,变量本身的静态类型不会被改变,且最终的静态类型是在编译器可知的。而实际类型变化的结果在运行期才可确定,编译器在编译时并不知道一个对象的实际类型是什么。虚拟机在重载时是通过参数的静态类型作为判定依据的,依据静态类型分派的方式称为静态分派,体现在方法重载,而动态分派体现在方法重写。动态分派的方法版本选择过程最常用的优化方法是为类在方法区中建立一个虚方法表,使用虚方法表索引来代替元数据查找以提高性能。需方法表中存放着各个方法的实际入口地址,如果某个方法在子类中没有被重写,那么子类中的虚方法表里面的地址入口和父类相同的方法的地址入口是一致的,如果重写了这个方法,那么方法入口地址将会代替入口地址
虚拟机类加载机制
Android类加载器ClassLoader

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容