2019独角兽企业重金招聘Python工程师标准>>>

类从虚拟机内存加载到从内存卸载,经历的生命周期是:加载,验证,准备,解析,初始化,使用,卸载这几个阶段, 其中验证,解析,初始化被称为 连接过程(Linking).

(打算这块和类加载原理后再看class文件结构那篇)

除了解析和使用,其他的过程基本顺序就是这样, 解析可以是在初始化完成之后,这是为了运行时动态绑定。

在虚拟机规范中定义了5中情况(有且只有)必须对类进行初始化(之前进行过,加载,验证,准备):

1.碰到new,getstatic,putstatic,invokestatic这4条字节码指令时,如果类没有进行初始化时,

2.在使用反射时,如果类没有初始化。

3.在这个类的父类没有初始化时。

4.虚拟机启动时指定的Main类,即主类。

5.使用动态语言支持时,特定的方法句柄对应的类没有初始化时(REF_getStatic,REF_putStatic,REF_invokeStatic)

类的加载

1.获取定义一个类的二进制流,二进制流可以是从网络获取,zip包,jar包都可以,也出现在jsp,动态代理(java.lang.reflect.Proxy)等。

2.将二进制流代表的静态存储结构转化为方法区运行时数据结构。

3.内存中生成代表这个类的java.lang.class对象,作为方法区这个类的各种数据访问入口。

非数组类是开发者可控性最强的,它可以有系统提供的classLoader加载,也可以有用户自定义的classLoader加载,而数组类是由虚拟机直接创建,但数组类的类元还是由classLoader来加载。数组类的创建过程遵循一下原则:

1.如果数组的组件类型(类似与 Foo[] fooArray)为引用类型,数组将在该组件类型的类加载器的类名称空间上被标识。

2.如果数组的组件类型不是引用类型(如 int[]),java虚拟机将会把数组标记为与引导类加载器关联。

3.数组类的可见性与它的组件类型的可见性一致

加载完成后,在内存(并没有明确规定是在java堆中,class类对象比较特殊,在HotSpot虚拟机中这块内存指的是方法区内存)中实例化类的java.lang.class对象,这个对象作为程序访问方法区数据类型的外部接口,加载与连接过程是交叉进行的(因为连接过程中包含验证过程),其他连接过程与加载过程依然保持顺序执行。

验证:

java 的class文件并不一定是由java源码生成,它可以由十六进制编译器直接编写来产生,但java虚拟机不会去访问数组边界以外的数据,也不会将对象转换为没有实现的类,执行不存在的代码之类的事情。但这些都需要经过虚拟机的验证过程。也是防止虚拟机遭受恶意代码的攻击,如果要验证的流没有经过class文件格式的规范则抛出java.lang.VerifyError或其子类异常。详细的参考java虚拟机规范 . 大致的验证有以下4个阶段检验动作:

1.文件格式验证:

这个阶段验证字节码是否符合规范及该虚拟是否能处理,包括一下验证点:

是否以魔数0xCAFEBABEK开头。

主,次版本是否在当前虚拟机处理范围内。

常量池是否含有不被支持的常量类型等。

远不止这些,不过这个验证通过后字节码才能进入内存的方法区并转换为运行时的数据结构。

2.元数据验证:

主要是检测元数据是否符合java规范如:

这个类是否有父类。

类是否继承了不允许继承的类。

是否实现了接口的所有方法等。

3.字节码验证(应该可以理解为方法的合法性验证):

整个验证过程中最复杂的一步,主要是通过数据流和控制流分析,程序语法的合法性,确保符合逻辑的,在第二阶段对元数据验证结束后,这个阶段主要是对类的方法体进行验证,保证运行时不会危害虚拟机 ,如:

1.保证操作数栈的数据类型与指令代码序列配合工作且不会出现栈中是int类型,使用时却以long的方式载入到本地变量表,

2.保证跳转指令不会跳转到方法体以外的字节码指令上。

3.保证类型转换是有效的。比如把子类对象赋值给父类对象这是安全的,但把父类对象赋值给子类的,甚至把对象赋值给与之不相干的类,前者是危险的后者是不合法的。

但这个过程之后也并不能完全保证它是安全的。 (关键字:StackMapTable, -XX:-UseSpliteVerifier, -XX:+FailOverToOldVerifier 以后用到的时候在看。先记下来:) )

4.符号引用验证(类,方法,字段是否可解析的验证):

目的是确保解析动作能正常运行,如果无法通过符号验证会抛出,java.lang.incompatibleClassChangeError的子类,如:java.lang.IllegalAccessError,java.lang.NoSuchFieldError,java.lang.NoSuchmethodError等,如果所运行的代码已经被反复使用和验证过,可以考虑使用-Xverify:none参数关闭大部分类验证,以缩短类加载时间。

通常需要校验的内容:

1.符号引用中通过字符串描述的全限定名是否能找到对应的类。

2.在指定的类中是否存在符合方法的字段描述符以及简单名称所描述的方法字段(应该是能否根据字段描述找到方法这个意思)。

3.符号引用中的类,字段,方法是否可被当前类访问(修饰符修饰的访问可能行)。

准备:

准备阶段正式为类的变量分配内存,这里的内存指的是方法区,变量是静态变量,非静态变量和类对象实例都是在java堆中分配内存,如:

static int value=123

在准备阶段value的值为0,这个时候方法还没有执行,只有经历初始化阶段value的值才会是123(putstatic是编译后存放于类构造器<clinit>()方法中),基本数据类型为 数字的都是0, 0f,0l,0d等,char为\u0000,String 不是基本类型,reference是null, 如果以上int 被final修饰过,则准备阶段虚拟机会根据ConstantValue的设置将value设置为123

解析:

解析阶段是把符号引用转换为直接引用的过程。

符号引用:

以一组符号来描述所引用目标(java类并不知道引用类的实际内存地址,因此只能使用符号引用来代替),与虚拟机的内存布局无关,引用的目标不一定已经加载到内存,各种虚拟机实现的内存布局可以不相同,但是他们能接受的符号引用必须相同,符号引用的字面量形式明确定义在java虚拟机规范class文件中。

直接引用:

直接引用可以是指向目标的指针,偏移量,或是能间接定位的目标句柄。与内存布局相关,能直接引用说明对象已经存在与内存中,不同的虚拟机实例上翻译出来的直接引用可能不一样。

虚拟机根据需要判断,是在类加载时对常量池中符号做解析 ,解析字段,方法,接口的过程中会去class_index中索引所属的类 ,并解析类符号,在此过程中如果出错,都会导致以上解析无法继续进行。

1.类或接口解析:

如果代码所处的类为A, 要将为解析过的符号引用N解析为一个类或接口C的直接引用(也就是类A 里有类C的引用)。需要一下步骤。

1.如果这个类C不是数组类型,那虚拟机会把符号引用的全限名传递给类D加载器去加载,可能会触发其他相关类的加载动作,如:该类的父类或实现接口。如果加载过程中出现任何异常,解析会失败。

2.如果C是数组,且元素为对象类型,则会去加载该对象,之后虚拟机生成一个代表此数组纬度和元素的数组对象。

3.上面的步骤完成后要进行符号引用验证,确定D是否有对C的访问全限,如果不具有则抛出java.lang.illgalAcessError。

2.字段解析:

解析一个为被解析过的字段引用,首先会对字段表内class_index中索引的CONSTANT_CLASS_info符号引用做解析,字段所属的类或接口符号引用的过程出现异常,会导致字段符号引用的失败。若成功则有以下步骤:

1.如果一个类本身包含了简单名称和字段描述都与目标字段匹配,则返回这个字段引用,查找结束。

2.否则去查找C是否实现了接口,如果是会往上递归查询,如果存在名称和字段描述都匹配,返回引用查找结束。

3.否则去查找父类当中是否存在,如果存在名称和字段描述都匹配,返回引用查找结束。

4.如果父类,接口都没有查到,抛出java.lang.NoSuchFieldError异常。

若找到了但是没有访问全限,则抛出java.lang.IllegalAccessError.实际上可能会更严格,若字段在父类和接口中多次出现,可能会拒绝编译。

3.方法解析;

方法解析中, 类方法解析和接口方法解析是有区别的,在常量池中这俩符号引用不同。

1.一旦一个类被解析为类而非接口,在类方法表中如果这个方法所属的类是接口,那么直接会抛出java.lang.incompatibleClassChangeError,换句话说是尝试引用一个纯接口而非实现类的方法时,会抛出此异常。

2.如果简单名称与描述符号都与目标匹配的方法,则返回这个方法的引用,查找结束。

3.如果第二部没有找到会往上递归去父类中寻找。

4.如果父类中未找到,则会去实现的接口或接口的父类中去查找,如果找到则会抛出,java.lang.AbstractMethodError,说明该类为抽象类。

5.如果以上都没有找到,则会抛出java.lang.NoSuchMethodError。

查找成功会去做权限验证,如果无访问全限则报:java.lang.IllegalAccessError异常

4:接口解析:

1.第一条与方法解析相反,查找是在接口方发表中查询, 如果这个方法包含的接口是类,则会报java.lang.incompatibleClassChangeError。

2.之后去判断简单名称和描述符号都与目标匹配,如果查到了就返回这个方法的直接引用。

3.否则一直会递归到object类,如果找到则返回直接引用,如果没找到则报java.lang.NoSuchMethodError异常。

在成功的情况下跟类方法解析成功一样都会去做全限检查

 初始化:

首先会执行<cinit>方法,cinit方法并不是一定产生 只有当类中有static和变量初始赋值时产生的,在子方法执行<cinit>之前保证执行完父类的<cinit>方法,接口中虽然没有静态变量块但是,仍然会产生<cinit>方法,接口执行cinit方法前不需要执行父类的cinit方法,接口的实现类初始化也不会执行接口的cinit方法,在多线程环境中,一个类的cinit执行会阻塞其他类执行这个类的cinit方法,而且只执行一次。

到此类的加载过程结束,这个时候的类才能真正去使用。

转载于:https://my.oschina.net/u/272065/blog/511660

java虚拟机学习(四)类的加载过程相关推荐

  1. java虚拟机预先加载哪些类_Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  2. Java虚拟机中 类的加载过程

    Java中 类的加载过程 例如下面的一段简单的代码 public class HelloWorld {public static void main(String[] args) {System.ou ...

  3. 深入理解Java虚拟机二(类加载器和类的加载过程)

    类加载器子系统作用 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识. ClassLoader只负责class文件的加载,至于它是否可以运行,则由Ex ...

  4. Java类的加载过程详解 面试高频!!!值得收藏!!!

    受多种情况的影响,又开始看JVM 方面的知识. 1.Java 实在过于内卷,没法不往深了学. 2.面试题问的多,被迫学习. 3.纯粹的好奇. 很喜欢一句话: 八小时内谋生活,八小时外谋发展. 望别日与 ...

  5. JVM学习笔记之-类加载子系统,类的加载与类的加载过程,双亲委派机制

    一 类加载器与类加载过程 类加载子系统作用 类加载器子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识. ClassLoader只负责class文件的加载,至于 ...

  6. Java常见面试题:类的加载过程

    程序员看似光鲜的就业前景面前,逃不过的是层层的面试,想要进前沿的大公司没有个五六七八面,是不可能滴!而找工作的首个关卡就是笔试,想要获得高薪工作的小伙伴,先刷一波面试题吧! 今天给大家分享的面试题是- ...

  7. java类加载过程_面试官:java类的加载过程

    Java 类加载机制 类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载七个阶段. 其中类加载过程包括加载.验证.准备.解析和初始化五个阶段. 类的加载 ...

  8. Java类的加载过程,类加载器,双亲委派原则

    Java一个类的加载过程: 1.加载 a.jvm会根据类名找到对应的类文件 b.进行文件内容读取 2.链接 链接主要是验证类中数据是否合法,然后把刚加载进来的类和其他类的关系建立清楚,主要有以下几个步 ...

  9. 类加载器 java委托机制_解析Java虚拟机中类的初始化及加载器的父委托机制

    类的初始化 在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值. 在程序中,静态变量的初始化有两种途径: 1.在静态变量的声明处进行初始化: 2.在静态代码块中进行初始化. 没有 ...

  10. Java 类的加载过程

    Java 类的加载过程 当程序主动使用某个类时,如果该类还没有加载到内存中,则通过以下三个步骤对类进行加载初始化: 类的加载:将类的class文件读入内存,并为之创建一个java.lang.Class ...

最新文章

  1. JS--屏蔽浏览器右键菜单
  2. 基于模块度的社团检测算法
  3. 分布式文件系统—HDFS—IDEA的Hadoop可视化插件BigDataTools
  4. HUAWEI nova 青春版闪速快充,让追剧不再断电
  5. 记录一次iOS11大标题不滚动的问题
  6. CodeIgniter中引用某一个表情(smiley)
  7. 深入浅出话VC++(2)——MFC的本质
  8. mysql5.718免安装教程_mysql5.7.18版本免安装配置教程
  9. MS 的IOC容器(ObjectBuilder)?
  10. Python代码刷博客访问量
  11. arduino简易电子琴
  12. 车牌限行C语言雾霾指数,算法设计与程序实现判断车牌的单双号(背景知识)为了保障空气质量,减少雾霾,某市决定当空气污染严重时,实行汽车单双号限行,违反规定的车辆将予以处罚。如何让高清摄像-组卷网...
  13. 广西北部湾经济区升级发展 全面对接粤港澳大湾区
  14. 【模板】最小费用最大流
  15. MySQL备份恢复-mysqldump/xbk
  16. 【菜鸟零基础学习笔记】Day22-os模块
  17. CDN可以防护什么种类的攻击?
  18. springboot集成cas3.5.2
  19. 【ESP32_8266_BT篇(二)】Beacon信标广播
  20. js100以内既能被3整除,也能被7整除的数

热门文章

  1. DPM全方位保护SQL Server,DPM2007系列之五
  2. MySQL Index Condition Pushdown 原理与解析
  3. 【poe设备加电配置】
  4. 三剑客”之Swarm应用数据持久化管理(volume 、bind 、 nfs)
  5. Exchange2013公用文件夹
  6. withCredentials--相同主域跨域解决方法
  7. xCode中工程相关的一些处理:一个工程包含多个Target的用途和使用方法【转】...
  8. ORACLE数据库备份
  9. [C#]统计文本文件txt中的行数(快速读取)
  10. visual studio 设计第一个WinForm小程序